Add split_var metacomment to assist UNOPTFLAT fixes, #2066.

This commit is contained in:
Wilson Snyder 2020-02-28 19:15:08 -05:00
parent c6b755a12e
commit 4878fe3a1f
22 changed files with 4058 additions and 22 deletions

View File

@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.029 devel
** Add split_var metacomment to assist UNOPTFLAT fixes, #2066. [Yutetsu TAKATSUKASA]
*** Add +verilator+noassert flag to disable assertion checking. [Tobias Wölfel]
*** Add check for assertOn for asserts, #2162. [Tobias Wölfel]

View File

@ -3420,6 +3420,33 @@ behavior. See the test_regress/t/t_dpi_display.v file for an example.
Same as C<sformat> in configuration files, see L</"CONFIGURATION FILES">
for more information.
=item /*verilator split_var*/
Attached to a variable or a net declaration to break the variable into
multiple pieces typically to resolve UNOPTFLAT performance issues.
Typically the variables to attach this to are recommeded by Verilator
itself, see UNOPTFLAT below.
For example, Verilator will internally convert a variable with the
metacomment such as:
logic [7:0] x [0:1] /*verilator split_var*/;
To:
logic [7:0] x__BRA__0__KET__ /*verilator split_var*/;
logic [7:0] x__BRA__1__KET__ /*verilator split_var*/;
Note that the generated packed variables retain the split_var metacomment
because they may be split into further smaller pieces accorting to the
access patterns.
This only supports unpacked arrays, packed arrays, and packed structs of
integer types (reg, logic, bit, byte, int...); otherwise if a split was
requested but cannot occur a SPLITVAR warning is issued. Splitting large
arrays may slow donw the Verilation speed, so use this only on variables
that require it.
=item /*verilator tag <text...>*/
Attached after a variable or structure member to indicate opaque (to
@ -4383,6 +4410,29 @@ Ignoring this warning may make Verilator simulations differ from other
simulators, if the increased precision of real affects your model or DPI
calls.
=item SPLITVAR
Warns that a variable with a C<split_var> metacomment was not split.
Some possible reasons for this are:
* The datatype of the variable is not supported for splitting. (e.g. is a
real).
* The access pattern of the variable can not be determined
statically. (e.g. is accessed as a memory).
* The index of the array exceeds the array size.
* The variable is accessed from outside using dotted reference.
(e.g. top.instance0.variable0 = 1).
* The variable is not declared in a module, but in a package or an
interface.
* The variable is a parameter, localparam, genvar, or queue.
* The variable is tirstate or bidirectional. (e.g. inout or ref).
=item STMTDLY
Warns that you have a statement with a delayed time in front of it, for
@ -4514,6 +4564,11 @@ being generated from an always statement that consumed high bits of the
same bus processed by another series of always blocks. The fix is the
same; split it into two separate signals generated from each block.
Another way to resolve this warning is to add a C<split_var> metacomment
described above. This will cause the variable to be split internally,
potentially resolving the conflict. If you run with --report-unoptflat
Verilator will suggest possible candidates for C<split_var>.
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.

View File

@ -233,6 +233,7 @@ RAW_OBJS = \
V3Slice.o \
V3Split.o \
V3SplitAs.o \
V3SplitVar.o \
V3Stats.o \
V3StatsReport.o \
V3String.o \

View File

@ -326,7 +326,8 @@ public:
VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv
VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat
VAR_CLOCKER, // V3LinkParse moves to AstVar::attrClocker
VAR_NO_CLOCKER // V3LinkParse moves to AstVar::attrClocker
VAR_NO_CLOCKER, // V3LinkParse moves to AstVar::attrClocker
VAR_SPLIT_VAR // V3LinkParse moves to AstVar::attrSplitVar
};
enum en m_e;
const char* ascii() const {
@ -342,7 +343,7 @@ public:
"VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC",
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW",
"VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER",
"VAR_NO_CLOCKER"
"VAR_NO_CLOCKER", "VAR_SPLIT_VAR"
};
return names[m_e];
}

View File

@ -1456,6 +1456,7 @@ private:
bool m_attrScBv:1; // User force bit vector attribute
bool m_attrIsolateAssign:1;// User isolate_assignments attribute
bool m_attrSFormat:1;// User sformat attribute
bool m_attrSplitVar:1; // declared with split_var metacomment
bool m_fileDescr:1; // File descriptor
bool m_isConst:1; // Table contains constant data
bool m_isStatic:1; // Static variable
@ -1478,7 +1479,7 @@ private:
m_sigUserRdPublic = false; m_sigUserRWPublic = false;
m_funcLocal = false; m_funcReturn = false;
m_attrClockEn = false; m_attrScBv = false;
m_attrIsolateAssign = false; m_attrSFormat = false;
m_attrIsolateAssign = false; m_attrSFormat = false; m_attrSplitVar = false;
m_fileDescr = false; m_isConst = false;
m_isStatic = false; m_isPulldown = false; m_isPullup = false;
m_isIfaceParent = false; m_isDpiOpenArray = false;
@ -1582,6 +1583,7 @@ public:
void attrScBv(bool flag) { m_attrScBv = flag; }
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
void attrSFormat(bool flag) { m_attrSFormat = flag; }
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
void usedClock(bool flag) { m_usedClock = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@ -1658,6 +1660,7 @@ public:
bool attrFileDescr() const { return m_fileDescr; }
bool attrScClocked() const { return m_scClocked; }
bool attrSFormat() const { return m_attrSFormat; }
bool attrSplitVar() const { return m_attrSplitVar; }
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
VVarAttrClocker attrClocker() const { return m_attrClocker; }
virtual string verilogKwd() const;

View File

@ -102,6 +102,7 @@ public:
REDEFMACRO, // Redefining existing define macro
SELRANGE, // Selection index out of range
SHORTREAL, // Shortreal not supported
SPLITVAR, // Cannot split the variable
STMTDLY, // Delayed statement
SYMRSVDWORD, // Symbol is Reserved Word
SYNCASYNCNET, // Mixed sync + async reset
@ -153,7 +154,7 @@ public:
"MULTIDRIVEN", "MULTITOP",
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PROCASSWIRE",
"REALCVT", "REDEFMACRO",
"SELRANGE", "SHORTREAL", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"TICKCOUNT",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSED",

View File

@ -2057,6 +2057,12 @@ private:
AstVarXRef* refp = new AstVarXRef(nodep->fileline(), nodep->name(),
m_ds.m_dotText, false); // lvalue'ness computed later
refp->varp(varp);
if (varp->attrSplitVar()) {
refp->v3warn(SPLITVAR, varp->prettyNameQ()
<< " has split_var metacomment but will not be split because"
<< " it is accessed from another module via a dot.");
varp->attrSplitVar(false);
}
m_ds.m_dotText = "";
if (m_ds.m_unresolved && m_ds.m_unlinkedScope) {
string dotted = refp->dotted();

View File

@ -305,6 +305,16 @@ private:
m_varp->attrSFormat(true);
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
else if (nodep->attrType() == AstAttrType::VAR_SPLIT_VAR) {
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
if (!VN_IS(m_modp, Module)) {
m_varp->v3warn(SPLITVAR, m_varp->prettyNameQ() << " has split_var metacomment, "
"but will not be split because it is not declared in a module.");
} else {
m_varp->attrSplitVar(true);
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
else if (nodep->attrType() == AstAttrType::VAR_SC_BV) {
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
m_varp->attrScBv(true);

View File

@ -91,6 +91,7 @@
#include "V3Partition.h"
#include "V3PartitionGraph.h"
#include "V3SenTree.h"
#include "V3SplitVar.h"
#include "V3Stats.h"
#include "V3Order.h"
@ -892,32 +893,48 @@ private:
m_graph.userClearVertices();
// May be very large vector, so only report the "most important"
// elements. Up to 10 of the widest
std::cerr<<V3Error::msgPrefix()
<<" Widest candidate vars to split:"<<endl;
std::cerr << V3Error::warnMore() << "... Widest candidate vars to split:" << endl;
std::stable_sort(m_unoptflatVars.begin(), m_unoptflatVars.end(), OrderVarWidthCmp());
vl_unordered_set<const AstVar*> canSplitList;
int lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10;
for (int i = 0; i < lim; i++) {
OrderVarStdVertex* vsvertexp = m_unoptflatVars[i];
AstVar* varp = vsvertexp->varScp()->varp();
std::cerr<<V3Error::msgPrefix()<<" "
<<varp->fileline()<<" "<<varp->prettyName()<<std::dec
<<", width "<<varp->width()<<", fanout "
<<vsvertexp->fanout()<<endl;
const bool canSplit = V3SplitVar::canSplitVar(varp);
std::cerr << V3Error::warnMore() << " " << varp->fileline() << " "
<< varp->prettyName() << std::dec << ", width " << varp->width()
<< ", fanout " << vsvertexp->fanout();
if (canSplit) {
std::cerr <<", can split_var";
canSplitList.insert(varp);
}
std::cerr << std::endl;
}
// Up to 10 of the most fanned out
std::cerr<<V3Error::msgPrefix()
<<" Most fanned out candidate vars to split:"<<endl;
std::cerr << V3Error::warnMore()
<< "... Most fanned out candidate vars to split:" << endl;
std::stable_sort(m_unoptflatVars.begin(), m_unoptflatVars.end(),
OrderVarFanoutCmp());
lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10;
for (int i = 0; i < lim; i++) {
OrderVarStdVertex* vsvertexp = m_unoptflatVars[i];
AstVar* varp = vsvertexp->varScp()->varp();
std::cerr<<V3Error::msgPrefix()<<" "
<<varp->fileline()<<" "<<varp->prettyName()
<<", width "<<std::dec<<varp->width()
<<", fanout "<<vsvertexp->fanout()<<endl;
const bool canSplit = V3SplitVar::canSplitVar(varp);
std::cerr << V3Error::warnMore() << " " << varp->fileline() << " "
<< varp->prettyName() << ", width " << std::dec << varp->width()
<< ", fanout " << vsvertexp->fanout();
if (canSplit) {
std::cerr << ", can split_var";
canSplitList.insert(varp);
}
std::cerr<<endl;
}
if (!canSplitList.empty()) {
std::cerr << V3Error::warnMore()
<< "... Suggest add /*verilator split_var*/ to appropriate variables above."
<< std::endl;
}
V3Stats::addStat("Order, SplitVar, candidates", canSplitList.size());
m_unoptflatVars.clear();
}

1310
src/V3SplitVar.cpp Normal file

File diff suppressed because it is too large Load Diff

39
src/V3SplitVar.h Normal file
View File

@ -0,0 +1,39 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break variables into separate words to avoid UNOPTFLAT
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2020 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 _V3SPLITVAR_H_
#define _V3SPLITVAR_H_ 1
//============================================================================
class AstNetlist;
class AstVar;
class V3SplitVar {
public:
// Split variables marked with split_var metacomment.
static void splitVariable(AstNetlist* nodep);
// Return true if the variable can be split.
// This check is not perfect.
static bool canSplitVar(const AstVar* varp);
};
#endif // Guard

View File

@ -82,6 +82,7 @@
#include "V3Slice.h"
#include "V3Split.h"
#include "V3SplitAs.h"
#include "V3SplitVar.h"
#include "V3Stats.h"
#include "V3String.h"
#include "V3Subst.h"
@ -175,12 +176,14 @@ static void process() {
V3Const::constifyAllLint(v3Global.rootp());
if (!v3Global.opt.xmlOnly()) {
// Split packed variables into multiple pieces to resolve UNOPTFLAT.
// should be after constifyAllLint() which flattens to 1D bit vector
V3SplitVar::splitVariable(v3Global.rootp());
// Remove cell arrays (must be between V3Width and scoping)
V3Inst::dearrayAll(v3Global.rootp());
V3LinkDot::linkDotArrayed(v3Global.rootp());
}
if (!v3Global.opt.xmlOnly()) {
// 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

View File

@ -704,6 +704,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"/*verilator public_flat_rd*/" { FL; return yVL_PUBLIC_FLAT_RD; }
"/*verilator public_flat_rw*/" { FL; return yVL_PUBLIC_FLAT_RW; } // The @(edge) is converted by the preproc
"/*verilator public_module*/" { FL; return yVL_PUBLIC_MODULE; }
"/*verilator split_var*/" { FL; return yVL_SPLIT_VAR; }
"/*verilator sc_clock*/" { FL; return yVL_CLOCK; }
"/*verilator clocker*/" { FL; return yVL_CLOCKER; }
"/*verilator no_clocker*/" { FL; return yVL_NO_CLOCKER; }

View File

@ -639,6 +639,7 @@ class AstSenTree;
%token<fl> yVL_PUBLIC_FLAT_RD "/*verilator public_flat_rd*/"
%token<fl> yVL_PUBLIC_FLAT_RW "/*verilator public_flat_rw*/"
%token<fl> yVL_PUBLIC_MODULE "/*verilator public_module*/"
%token<fl> yVL_SPLIT_VAR "/*verilator split_var*/"
%token<fl> yP_TICK "'"
%token<fl> yP_TICKBRA "'{"
@ -2282,6 +2283,7 @@ sigAttr<nodep>:
| yVL_ISOLATE_ASSIGNMENTS { $$ = new AstAttrOf($1,AstAttrType::VAR_ISOLATE_ASSIGNMENTS); }
| yVL_SC_BV { $$ = new AstAttrOf($1,AstAttrType::VAR_SC_BV); }
| yVL_SFORMAT { $$ = new AstAttrOf($1,AstAttrType::VAR_SFORMAT); }
| yVL_SPLIT_VAR { $$ = new AstAttrOf($1,AstAttrType::VAR_SPLIT_VAR); }
;
rangeListE<rangep>: // IEEE: [{packed_dimension}]

26
test_regress/t/t_split_var_0.pl Executable file
View File

@ -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 2003 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
scenarios(simulator => 1);
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
compile(
verilator_flags2 => ['--stats' . ($Self->{vltmt} ? ' --threads 6' : '')],
);
execute(
check_finished => 1,
);
file_grep($Self->{stats}, qr/SplitVar,\s+Split packed variables\s+(\d+)/i, 13);
file_grep($Self->{stats}, qr/SplitVar,\s+Split unpacked arrays\s+(\d+)/i, 23);
ok(1);
1;

View File

@ -0,0 +1,431 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Yutetsu TAKATSUKASA.
// If split_var pragma is removed, UNOPTFLAT appears.
module barshift_1d_unpacked #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out /*verilator split_var*/);
localparam OFFSET = -3;
logic [WIDTH-1:0] tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/;
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
if (shift[i]) begin
/*verilator lint_off ALWCOMBORDER*/
tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]};
/*verilator lint_on ALWCOMBORDER*/
end
else begin
tmp[i+1+OFFSET] = tmp[i+OFFSET];
end
end
endgenerate
assign tmp[0+OFFSET] = in;
assign out[WIDTH-1-:WIDTH-1] = tmp[DEPTH+OFFSET][WIDTH-1:1];
assign out[0] = tmp[DEPTH+OFFSET][0+:1];
endmodule
module barshift_1d_unpacked_le #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
localparam OFFSET = -3;
// almost same as above module, but tmp[smaller:bigger] here.
logic [WIDTH-1:0] tmp[OFFSET:DEPTH+OFFSET] /*verilator split_var*/;
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
if (shift[i]) begin
/*verilator lint_off ALWCOMBORDER*/
tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]};
/*verilator lint_on ALWCOMBORDER*/
end
else begin
tmp[i+1+OFFSET] = tmp[i+OFFSET];
end
end
endgenerate
assign tmp[0+OFFSET] = in;
assign out = tmp[DEPTH+OFFSET];
endmodule
module barshift_1d_unpacked_struct0 #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
localparam OFFSET = 1;
typedef struct packed { logic [WIDTH-1:0] data; } data_type;
data_type tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/;
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
if (shift[i]) begin
/*verilator lint_off ALWCOMBORDER*/
tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]};
/*verilator lint_on ALWCOMBORDER*/
end
else begin
tmp[i+1+OFFSET] = tmp[i+OFFSET];
end
end
endgenerate
assign tmp[0+OFFSET] = in;
assign out = tmp[DEPTH+OFFSET];
endmodule
module barshift_2d_unpacked #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
localparam OFFSET = 1;
localparam N = 3;
reg [WIDTH-1:0] tmp0[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp1[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp2[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1];
reg [WIDTH-1:0] tmp3[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp4[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp5[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1];
reg [WIDTH-1:0] tmp6[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp7[DEPTH+OFFSET+1:OFFSET+1][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp8[DEPTH+OFFSET+3:OFFSET-1][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp9[DEPTH+OFFSET+3:OFFSET+3][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp10[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
// because tmp11 is not split for testing mixture usage of split_var and no-spliv_ar,
// UNOPTFLAT appears, but it's fine.
/*verilator lint_off UNOPTFLAT*/
reg [WIDTH-1:0] tmp11[-1:1][DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1];
/*verilator lint_on UNOPTFLAT*/
reg [WIDTH-1:0] tmp12[-1:0][DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
reg [WIDTH-1:0] tmp13[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/;
generate
for(genvar i = 0; i < DEPTH; ++i) begin
for(genvar j = OFFSET; j < N + OFFSET; ++j) begin
always_comb
if (shift[i]) begin
/*verilator lint_off ALWCOMBORDER*/
tmp0[i+1+OFFSET][j] = {tmp0[i+OFFSET][j][(1 << i)-1:0], tmp0[i+OFFSET][j][WIDTH-1:(2**i)]};
/*verilator lint_on ALWCOMBORDER*/
end
else begin
tmp0[i+1+OFFSET][j] = tmp0[i+OFFSET][j];
end
end
end
for(genvar j = OFFSET; j < N + OFFSET; ++j) begin
assign tmp0[0 + OFFSET][j] = in;
end
endgenerate
assign tmp1 = tmp0; // split both side
assign tmp2 = tmp1; // split only rhs
assign tmp3 = tmp2; // split only lhs
always_comb tmp4 = tmp3; // split both side
always_comb tmp5 = tmp4; // split only rhs
always_comb tmp6 = tmp5; // split only lhs
assign tmp7 = tmp6;
assign tmp8[DEPTH+OFFSET+1:OFFSET+1] = tmp7;
assign tmp9 = tmp8[DEPTH+OFFSET+1:OFFSET+1];
assign tmp10[DEPTH+OFFSET:OFFSET] = tmp9[DEPTH+OFFSET+3:OFFSET+3];
assign tmp11[1] = tmp10;
assign tmp11[-1] = tmp11[1];
assign tmp11[0] = tmp11[-1];
assign tmp12 = tmp11[0:1];
assign out = tmp12[1][DEPTH+OFFSET][OFFSET];
endmodule
module barshift_1d_unpacked_struct1 #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
localparam OFFSET = 2;
typedef struct packed { int data; } data_type;
data_type tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/;
localparam [32-WIDTH-1:0] pad = 0;
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
if (shift[i]) begin
/*verilator lint_off ALWCOMBORDER*/
tmp[i+1+OFFSET] = {pad, tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]};
/*verilator lint_on ALWCOMBORDER*/
end
else begin
tmp[i+1+OFFSET] = tmp[i+OFFSET];
end
end
endgenerate
assign tmp[0+OFFSET] = {pad, in};
assign out = tmp[DEPTH+OFFSET][WIDTH-1:0];
endmodule
module barshift_2d_packed_array #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
localparam OFFSET = -2;
/*verilator lint_off LITENDIAN*/
reg [OFFSET:DEPTH+OFFSET][WIDTH-1:0] tmp /*verilator split_var*/;
/*verilator lint_on LITENDIAN*/
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
/*verilator lint_off ALWCOMBORDER*/
if (shift[i]) begin
tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]};
end
else begin
tmp[i+1+OFFSET][1:0] = tmp[i+OFFSET][1:0];
tmp[i+1+OFFSET][WIDTH-1:2] = tmp[i+OFFSET][WIDTH-1:2];
end
/*verilator lint_on ALWCOMBORDER*/
end
endgenerate
assign tmp[0+OFFSET] = in;
assign out = tmp[DEPTH+OFFSET];
endmodule
module barshift_2d_packed_array_le #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
localparam OFFSET = -2;
/*verilator lint_off LITENDIAN*/
reg [OFFSET:DEPTH+OFFSET][OFFSET:WIDTH-1+OFFSET] tmp /*verilator split_var*/;
/*verilator lint_on LITENDIAN*/
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
/*verilator lint_off ALWCOMBORDER*/
if (shift[i]) begin
tmp[i+1+OFFSET] = {tmp[i+OFFSET][WIDTH-(2**i)+OFFSET:WIDTH-1+OFFSET], tmp[i+OFFSET][OFFSET:WIDTH-(2**i)-1+OFFSET]};
end
else begin // actulally just tmp[i+1+OFFSET] = tmp[i+OFFSET]
tmp[i+1+OFFSET][0+OFFSET:2+OFFSET] = tmp[i+OFFSET][0+OFFSET:2+OFFSET];
tmp[i+1+OFFSET][3+OFFSET] = tmp[i+OFFSET][3+OFFSET];
{tmp[i+1+OFFSET][4+OFFSET],tmp[i+1+OFFSET][5+OFFSET]} = {tmp[i+OFFSET][4+OFFSET], tmp[i+OFFSET][5+OFFSET]};
{tmp[i+1+OFFSET][7+OFFSET],tmp[i+1+OFFSET][6+OFFSET]} = {tmp[i+OFFSET][7+OFFSET], tmp[i+OFFSET][6+OFFSET]};
end
/*verilator lint_on ALWCOMBORDER*/
end
endgenerate
assign tmp[0+OFFSET] = in;
assign out = tmp[DEPTH+OFFSET];
endmodule
module barshift_1d_packed_struct #(localparam DEPTH = 3, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
typedef struct packed {
logic [WIDTH-1:0] v0, v1, v2, v3;
} data_type;
wire data_type tmp /*verilator split_var*/;
assign tmp.v0 = in;
assign tmp.v1 = shift[0] == 1'b1 ? {tmp.v0[(1 << 0)-1:0], tmp.v0[WIDTH-1:2**0]} : tmp.v0;
assign tmp.v2 = shift[1] == 1'b1 ? {tmp.v1[(1 << 1)-1:0], tmp.v1[WIDTH-1:2**1]} : tmp.v1;
assign tmp.v3 = shift[2] == 1'b1 ? {tmp.v2[(1 << 2)-1:0], tmp.v2[WIDTH-1:2**2]} : tmp.v2;
assign out = tmp.v3;
endmodule
module barshift_bitslice #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH)
(input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out);
/*verilator lint_off LITENDIAN*/
wire [0:WIDTH*(DEPTH+1) - 1] tmp /*verilator split_var*/;
/*verilator lint_on LITENDIAN*/
generate
for(genvar i = 0; i < DEPTH; ++i) begin
always_comb
if (shift[i]) begin
tmp[WIDTH*(i+1):WIDTH*(i+1+1)-1] = {tmp[WIDTH*(i+1)-(1<<i):WIDTH*(i+1)-1], tmp[WIDTH*i:WIDTH*i+((WIDTH-1) - (2**i))]};
end
else begin
tmp[WIDTH*(i+1):WIDTH*(i+1+1)-1] = tmp[WIDTH*i:WIDTH*(i+1)-1];
end
end
endgenerate
assign tmp[WIDTH*0:WIDTH*(0+1)-1] = in;
assign out = tmp[WIDTH*DEPTH:WIDTH*(DEPTH+1)-1];
endmodule
module var_decl_with_init();
/*verilator lint_off LITENDIAN*/
logic [-1:30] var0 /* verilator split_var */ = {4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7};
logic [-1:30] var2 /* verilator split_var */;
/*verilator lint_on LITENDIAN*/
logic [30:-1] var1 /* verilator split_var */ = {4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7};
logic [30:-1] var3 /* verilator split_var */;
initial begin
var2[-1:2] = 4'd2;
var3[2:-1] = 4'd3;
$display("%x %x", var0, var1);
$display("%x %x", var2, var3);
var0[-1:5] = 7'd0;
var1[10:3] = 8'd2;
end
endmodule
module t_array_rev(clk); // from t_array_rev.v
input clk;
integer cyc=0;
// verilator lint_off LITENDIAN
logic arrd [0:1] /*verilator split_var*/ = '{ 1'b1, 1'b0 };
// verilator lint_on LITENDIAN
logic y0, y1;
logic localbkw [1:0]/*verilator split_var*/ ;
arr_rev arr_rev_u
(
.arrbkw (arrd),
.y0(y0),
.y1(y1)
);
always @ (posedge clk) begin
if (arrd[0] != 1'b1) $stop;
if (arrd[1] != 1'b0) $stop;
localbkw = arrd;
if (localbkw[0] != 1'b0) $stop;
if (localbkw[1] != 1'b1) $stop;
if (y0 != 1'b0) $stop;
if (y1 != 1'b1) $stop;
end
endmodule
module arr_rev
(
input var logic arrbkw [1:0]/*verilator split_var*/ ,
output var logic y0,
output var logic y1
);
always_comb y0 = arrbkw[0];
always_comb y1 = arrbkw[1];
endmodule
module pack2unpack #(parameter WIDTH = 8)
(input wire [WIDTH-1:0] in/*verilator split_var*/, output wire out [WIDTH-1:0] /*verilator split_var*/);
generate
for (genvar i = 0; i < WIDTH; ++i) begin
assign out[i] = in[i];
end
endgenerate
endmodule
module unpack2pack #(parameter WIDTH = 8)
(input wire in [WIDTH-1:0] /*verilator split_var*/, output wire [WIDTH-1:0] out/*verilator split_var*/);
function automatic [1:0] to_packed0;
logic [1:0] tmp /*verilator split_var*/;
input logic in[1:0] /*verilator split_var*/;
tmp[1] = in[1];
tmp[0] = in[0];
return tmp;
endfunction
/* verilator lint_off UNOPTFLAT*/
task automatic to_packed1(input logic in[1:0] /*verilator split_var*/, output logic [1:0] out /*verilator split_var*/);
out[1] = in[1];
out[0] = in[0];
endtask
/* verilator lint_on UNOPTFLAT*/
generate
for (genvar i = 4; i < WIDTH; i += 4) begin
always @(*) begin
out[i+1:i] = to_packed0(in[i+1:i]);
out[i+3:i+2] = to_packed0(in[i+3:i+2]);
end
end
always_comb
to_packed1(.in(in[1:0]), .out(out[1:0]));
always_comb
to_packed1(.in(in[3:2]), .out(out[3:2]));
endgenerate
endmodule
module through #(parameter WIDTH = 8)
(input wire [WIDTH-1:0] in, output wire [WIDTH-1:0] out);
logic unpack_tmp [0:WIDTH-1] /*verilator split_var*/;
pack2unpack i_pack2unpack(.in(in), .out(unpack_tmp));
unpack2pack i_unpack2pack(.in(unpack_tmp), .out(out));
endmodule
module t(/*AUTOARG*/ clk);
input clk;
localparam DEPTH = 3;
localparam WIDTH = 2**DEPTH;
localparam NUMSUB = 9;
logic [WIDTH-1:0] in;
logic [WIDTH-1:0] out[0:NUMSUB-1];
logic [WIDTH-1:0] through_tmp;
logic [DEPTH-1:0] shift = 0;
// barrel shifter
barshift_1d_unpacked #(.DEPTH(DEPTH)) shifter0(.in(in), .out(out[0]), .shift(shift));
barshift_1d_unpacked_le #(.DEPTH(DEPTH)) shifter1(.in(in), .out(out[1]), .shift(shift));
barshift_1d_unpacked_struct0 #(.DEPTH(DEPTH)) shifter2(.in(in), .out(out[2]), .shift(shift));
barshift_2d_unpacked #(.DEPTH(DEPTH)) shifter3(.in(in), .out(out[3]), .shift(shift));
barshift_1d_unpacked_struct1 #(.DEPTH(DEPTH)) shifter4(.in(in), .out(out[4]), .shift(shift));
barshift_2d_packed_array #(.DEPTH(DEPTH)) shifter5(.in(in), .out(out[5]), .shift(shift));
barshift_2d_packed_array_le #(.DEPTH(DEPTH)) shifter6(.in(in), .out(out[6]), .shift(shift));
barshift_1d_packed_struct shifter7(.in(in), .out(out[7]), .shift(shift));
barshift_bitslice #(.DEPTH(DEPTH)) shifter8(.in(in), .out(out[8]), .shift(shift));
through #(.WIDTH(WIDTH)) though0 (.in(out[8]), .out(through_tmp));
var_decl_with_init i_var_decl_with_init();
t_array_rev i_t_array_rev(clk);
assign in = 8'b10001110;
/*verilator lint_off LITENDIAN*/
logic [7:0] [7:0] expc
= {8'b10001110, 8'b01000111, 8'b10100011, 8'b11010001,
8'b11101000, 8'b01110100, 8'b00111010, 8'b00011101};
/*verilator lint_on LITENDIAN*/
always @(posedge clk) begin : always_block
automatic bit failed = 0;
$display("in:%b shift:%d expc:%b", in, shift, expc[7-shift]);
for (int i = 0; i < NUMSUB; ++i) begin
if (out[i] != expc[7-shift]) begin
$display("Missmatch out[%d]:%b", i, out[i]);
failed = 1;
end
end
if (through_tmp != expc[7-shift]) begin
$display("Missmatch through_tmp:%b", through_tmp);
failed = 1;
end
if (failed) $stop;
if (shift == 7) begin
$write("*-* All Finished *-*\n");
$finish;
end
shift <= shift + 1;
end
endmodule

View File

@ -0,0 +1,65 @@
%Warning-SPLITVAR: t/t_split_var_1_bad.v:6: 'should_show_warning_global0' has split_var metacomment, but will not be split because it is not declared in a module.
logic [7:0] should_show_warning_global0 /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
... Use "/* verilator lint_off SPLITVAR */" and lint_on around source to disable this message.
%Warning-SPLITVAR: t/t_split_var_1_bad.v:7: 'should_show_warning_global1' has split_var metacomment, but will not be split because it is not declared in a module.
logic [7:0] should_show_warning_global1 [1:0] /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:10: 'should_show_warning_ifs0' has split_var metacomment, but will not be split because it is not declared in a module.
logic [7:0] should_show_warning_ifs0 /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:11: 'should_show_warning_ifs1' has split_var metacomment, but will not be split because it is not declared in a module.
logic [7:0] should_show_warning_ifs1 [1:0] /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:37: 'cannot_split1' has split_var metacomment but will not be split because it is accessed from another module via a dot.
i_sub0.cannot_split1[0] = 0;
^~~~~~~~~~~~~
%Warning-SELRANGE: t/t_split_var_1_bad.v:82: Selection index out of range: 13 outside 12:10
: ... In instance t.i_sub3
assign outwires[12] = inwires[13];
^
%Warning-WIDTH: t/t_split_var_1_bad.v:38: Operator ASSIGN expects 8 bits on the Assign RHS, but Assign RHS's FUNCREF 'bad_func' generates 32 bits.
: ... In instance t
i_sub0.cannot_split1[1] = bad_func(addr, rd_data0);
^
%Error: t/t_split_var_1_bad.v:71: Illegal assignment of constant to unpacked array
: ... In instance t.i_sub2
assign b = a[0];
^
%Warning-SPLITVAR: t/t_split_var_1_bad.v:50: 'cannot_split0' has split_var metacomment but will not be split because index cannot be determined statically.
: ... In instance t.i_sub0
rd_data = cannot_split0[addr];
^~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:82: 'inwires' has split_var metacomment but will not be split because index is out of range.
: ... In instance t.i_sub3
assign outwires[12] = inwires[13];
^~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:16: 'should_show_warning0' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic
: ... In instance t
real should_show_warning0 /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:17: 'should_show_warning1' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic
: ... In instance t
string should_show_warning1 /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:18: 'should_show_warning2' has split_var metacomment but will not be split because its bitwidth is 1
: ... In instance t
wire should_show_warning2 /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:29: 'inout_port' has split_var metacomment but will not be split because it is an inout port
: ... In instance t
function int bad_func(inout logic [3:0] inout_port /*verilator split_var*/ ,
^~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:30: 'ref_port' has split_var metacomment but will not be split because it is a ref argument
: ... In instance t
ref logic [7:0] ref_port /*verilator split_var*/ );
^~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:56: 'cannot_split_genvar' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic
: ... In instance t.i_sub1
genvar cannot_split_genvar /*verilator split_var*/ ;
^~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:59: 'cannot_split' has split_var metacomment but will not be split because its bit range cannot be determined statically.
: ... In instance t.i_sub1
rd_data = cannot_split[addr];
^
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
scenarios(simulator => 1);
compile(
fails => 1,
verilator_flags2 => ['--stats'],
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,84 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Yutetsu TAKATSUKASA.
logic [7:0] should_show_warning_global0 /* verilator split_var */;
logic [7:0] should_show_warning_global1 [1:0] /* verilator split_var */;
interface ifs;
logic [7:0] should_show_warning_ifs0 /* verilator split_var */;
logic [7:0] should_show_warning_ifs1 [1:0] /* verilator split_var */;
endinterface
module t();
// The following variables can not be splitted. will see warnings.
real should_show_warning0 /*verilator split_var*/;
string should_show_warning1 /*verilator split_var*/;
wire should_show_warning2 /*verilator split_var*/;
logic [3:0] addr;
logic [7:0] rd_data0, rd_data1, rd_data2;
sub0 i_sub0(.addr(addr), .rd_data(rd_data0));
sub1 i_sub1(.addr(addr), .rd_data(rd_data2));
sub2 i_sub2;
sub3 i_sub3;
ifs i_ifs();
function int bad_func(inout logic [3:0] inout_port /*verilator split_var*/,
ref logic [7:0] ref_port /*verilator split_var*/);
return 0;
endfunction
initial begin
addr = 0;
addr = 1;
i_sub0.cannot_split1[0] = 0;
i_sub0.cannot_split1[1] = bad_func(addr, rd_data0);
$finish;
end
endmodule
module sub0(input [3:0]addr, output logic [7:0] rd_data);
logic [7:0] cannot_split0[0:15] /*verilator split_var*/;
logic [7:0] cannot_split1[0:15] /*verilator split_var*/;
always_comb
rd_data = cannot_split0[addr];
endmodule
module sub1(input [3:0]addr, output logic [7:0] rd_data);
genvar cannot_split_genvar /*verilator split_var*/;
logic [15:0] [7:0] cannot_split /*verilator split_var*/;
always_comb
rd_data = cannot_split[addr];
endmodule
module sub2; // from t_bitsel_wire_array_bad.v
// a and b are arrays of length 1.
wire a[0:0] /* verilator split_var */ ; // Array of nets
wire b[0:0] /* verilator split_var */ ;
assign a = 1'b0; // Only net assignment allowed
assign b = a[0]; // Only net assignment allowed
endmodule
module sub3; // from t_select_bad_range3.v
logic [7:0] inwires [12:10] /* verilator split_var */;
wire [7:0] outwires [12:10] /* verilator split_var */;
assign outwires[10] = inwires[11];
assign outwires[11] = inwires[12];
assign outwires[12] = inwires[13]; // must be an error here
endmodule

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
scenarios(simulator => 1);
top_filename("t/t_split_var_0.v");
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
compile(
verilator_flags2 => ['--cc --trace --stats' . ($Self->{vltmt} ? ' --threads 6' : '')],
);
execute(
check_finished => 1,
);
vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename});
file_grep($Self->{stats}, qr/SplitVar,\s+Split packed variables\s+(\d+)/i, 12);
file_grep($Self->{stats}, qr/SplitVar,\s+Split unpacked arrays\s+(\d+)/i, 23);
ok(1);
1;

View File

@ -5,8 +5,9 @@
t/t_unoptflat_simple_2.v:14: Example path: t.x
t/t_unoptflat_simple_2.v:16: Example path: ASSIGNW
t/t_unoptflat_simple_2.v:14: Example path: t.x
%Warning-UNOPTFLAT: Widest candidate vars to split:
%Warning-UNOPTFLAT: t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10
%Warning-UNOPTFLAT: Most fanned out candidate vars to split:
%Warning-UNOPTFLAT: t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10
... Widest candidate vars to split:
t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10, can split_var
... Most fanned out candidate vars to split:
t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10, can split_var
... Suggest add /*verilator split_var*/ to appropriate variables above.
%Error: Exiting due to