Add /*verilator public[flat|flat_rd|flat_rw| ]*/ metacomments (#3894)

This commit is contained in:
Joseph Nwabueze 2023-02-03 08:47:55 -05:00 committed by GitHub
parent e16e9b89cd
commit c8be50d40b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 336 additions and 35 deletions

View File

@ -65,6 +65,7 @@ Joey Liu
John Coiner
John Demme
Jonathan Drolet
Joseph Nwabueze
Josh Redford
Julie Schwartz
Julien Margetts

View File

@ -446,6 +446,31 @@ or "`ifdef`"'s may break other tools.
Same as :option:`public_flat_rw` configuration file option.
.. option:: /*verilator&32;public_[|flat|flat_rd|flat_rw]_on [@(<edge_list>)]*/ (as scope)
Used to wrap multiple signals and parameters with the respective public attribute.
See attribute above for their respective behavior. Cannot be nested. e.g:
.. code-block:: sv
/*verilator public_flat_rw_on*/
logic clk;
logic rst;
parameter width = 8;
/* verilator public_off*/
logic data;
Is equivalent to:
.. code-block:: sv
logic clk /*verilator public_flat_rw*/;
logic rst /*verilator public_flat_rw*/;
parameter width /*verilator public_flat_rw*/ = 8;
logic data;
.. option:: /*verilator&32;public_off*/
Terminates the previous `/*verilator public*_on*/` directive; see above.
.. option:: /*verilator&32;public_module*/
Used after a module statement to indicate the module should not be

View File

@ -805,6 +805,10 @@ public:
bool isTemp() const {
return (m_e == BLOCKTEMP || m_e == MODULETEMP || m_e == STMTTEMP || m_e == XTEMP);
}
bool isVPIAccessible() const {
return (m_e == VAR || m_e == GPARAM || m_e == LPARAM || m_e == PORT || m_e == WIRE
|| m_e == TRI0 || m_e == TRI1);
}
};
constexpr bool operator==(const VVarType& lhs, const VVarType& rhs) VL_MT_SAFE {
return lhs.m_e == rhs.m_e;

View File

@ -187,7 +187,7 @@ public:
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
"MULTIDRIVEN", "MULTITOP","NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"MULTIDRIVEN", "MULTITOP", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",

View File

@ -240,6 +240,9 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name,
} else {
nodep->trace(allTracingOn(nodep->fileline()));
}
if (nodep->varType().isVPIAccessible()) {
nodep->addAttrsp(GRAMMARP->cloneScopedSigAttr());
}
// Remember the last variable created, so we can attach attributes to it in later parsing
GRAMMARP->m_varAttrp = nodep;

View File

@ -472,12 +472,16 @@ void V3PreProcImp::comment(const string& text) {
// else ignore the comment we don't recognize
} // else no assertions
} else if (vlcomment) {
string::size_type pos;
if ((pos = cmd.find("public_flat_rw")) != string::npos) {
if (VString::startsWith(cmd, "public_flat_rw")) {
// "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)"
cmd = cmd.substr(pos + std::strlen("public_flat_rw"));
while (isspace(cmd[0])) cmd = cmd.substr(1);
if (!printed) insertUnreadback("/*verilator public_flat_rw*/ " + cmd + " /**/");
string::size_type endOfCmd = std::strlen("public_flat_rw");
while (VString::isWordChar(cmd[endOfCmd])) ++endOfCmd;
string baseCmd = cmd.substr(0, endOfCmd);
string arg = cmd.substr(endOfCmd);
while (isspace(arg[0])) arg = arg.substr(1);
if (arg.size() && baseCmd == "public_flat_rw_on")
baseCmd += "_sns"; // different cmd for applying sensitivity
if (!printed) insertUnreadback("/*verilator " + baseCmd + "*/ " + arg + " /**/");
} else {
if (!printed) insertUnreadback("/*verilator " + cmd + "*/");
}

View File

@ -176,16 +176,14 @@ double VString::parseDouble(const string& str, bool* successp) {
return d;
}
static bool isWordChar(char c) { return isalnum(c) || c == '_'; }
string VString::replaceWord(const string& str, const string& from, const string& to) {
string result = str;
const size_t len = from.size();
UASSERT_STATIC(len > 0, "Cannot replace empty string");
for (size_t pos = 0; (pos = result.find(from, pos)) != string::npos; pos += len) {
// Only replace whole words
if (((pos > 0) && isWordChar(result[pos - 1])) || //
((pos + len < result.size()) && isWordChar(result[pos + len]))) {
if (((pos > 0) && VString::isWordChar(result[pos - 1])) || //
((pos + len < result.size()) && VString::isWordChar(result[pos + len]))) {
continue;
}
result.replace(pos, len, to);

View File

@ -122,6 +122,8 @@ public:
static bool startsWith(const string& str, const string& prefix);
// Predicate to check if 'str' ends with 'suffix'
static bool endsWith(const string& str, const string& suffix);
// Return true if char is valid character in word
static bool isWordChar(char c) { return isalnum(c) || c == '_'; }
};
//######################################################################

View File

@ -757,9 +757,15 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"/*verilator parallel_case*/" { FL; return yVL_PARALLEL_CASE; }
"/*verilator public*/" { FL; return yVL_PUBLIC; }
"/*verilator public_flat*/" { FL; return yVL_PUBLIC_FLAT; }
"/*verilator public_flat_on*/" { FL; return yVL_PUBLIC_FLAT_ON; }
"/*verilator public_flat_rd*/" { FL; return yVL_PUBLIC_FLAT_RD; }
"/*verilator public_flat_rd_on*/" { FL; return yVL_PUBLIC_FLAT_RD_ON; }
"/*verilator public_flat_rw*/" { FL; return yVL_PUBLIC_FLAT_RW; } // The @(edge) is converted by the preproc
"/*verilator public_flat_rw_on*/" { FL; return yVL_PUBLIC_FLAT_RW_ON; }
"/*verilator public_flat_rw_on_sns*/" { FL; return yVL_PUBLIC_FLAT_RW_ON_SNS; }
"/*verilator public_module*/" { FL; return yVL_PUBLIC_MODULE; }
"/*verilator public_on*/" { FL; return yVL_PUBLIC_ON; }
"/*verilator public_off*/" { FL; return yVL_PUBLIC_OFF; } // terminates previous 'verilator public*_on'
"/*verilator sc_bv*/" { FL; return yVL_SC_BV; }
"/*verilator sc_clock*/" { FL; yylval.fl->v3warn(DEPRECATED, "sc_clock is ignored"); FL_BRK; }
"/*verilator sformat*/" { FL; return yVL_SFORMAT; }

View File

@ -78,6 +78,7 @@ class V3ParseGrammar {
public:
AstVar* m_varAttrp = nullptr; // Current variable for attribute adding
AstRange* m_gateRangep = nullptr; // Current range for gate declarations
AstNode* m_scopedSigAttr = nullptr; // Pointer to default signal attribute
AstCase* m_caseAttrp = nullptr; // Current case statement for attribute adding
AstNodeDType* m_varDTypep = nullptr; // Pointer to data type for next signal declaration
AstNodeDType* m_memDTypep = nullptr; // Pointer to data type for next member declaration
@ -270,6 +271,19 @@ public:
}
return itemsp;
}
void setScopedSigAttr(AstNode* attrsp) {
if (m_scopedSigAttr) { // clearing set attribute
VL_DO_DANGLING(m_scopedSigAttr->deleteTree(), m_scopedSigAttr);
}
m_scopedSigAttr = attrsp;
}
void createScopedSigAttr(VAttrType vattrT) {
setScopedSigAttr(new AstAttrOf{PARSEP->lexFileline(), vattrT});
}
AstNode* cloneScopedSigAttr() const { return AstNode::cloneTreeNull(m_scopedSigAttr, true); }
};
const VBasicDTypeKwd LOGIC = VBasicDTypeKwd::LOGIC; // Shorthand "LOGIC"
@ -937,28 +951,34 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yD_WRITEMEMH "$writememh"
%token<fl> yD_WRITEO "$writeo"
%token<fl> yVL_CLOCKER "/*verilator clocker*/"
%token<fl> yVL_CLOCK_ENABLE "/*verilator clock_enable*/"
%token<fl> yVL_COVERAGE_BLOCK_OFF "/*verilator coverage_block_off*/"
%token<fl> yVL_FORCEABLE "/*verilator forceable*/"
%token<fl> yVL_FULL_CASE "/*verilator full_case*/"
%token<fl> yVL_HIER_BLOCK "/*verilator hier_block*/"
%token<fl> yVL_INLINE_MODULE "/*verilator inline_module*/"
%token<fl> yVL_ISOLATE_ASSIGNMENTS "/*verilator isolate_assignments*/"
%token<fl> yVL_NO_CLOCKER "/*verilator no_clocker*/"
%token<fl> yVL_NO_INLINE_MODULE "/*verilator no_inline_module*/"
%token<fl> yVL_NO_INLINE_TASK "/*verilator no_inline_task*/"
%token<fl> yVL_PARALLEL_CASE "/*verilator parallel_case*/"
%token<fl> yVL_PUBLIC "/*verilator public*/"
%token<fl> yVL_PUBLIC_FLAT "/*verilator public_flat*/"
%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_SC_BV "/*verilator sc_bv*/"
%token<fl> yVL_SFORMAT "/*verilator sformat*/"
%token<fl> yVL_SPLIT_VAR "/*verilator split_var*/"
%token<strp> yVL_TAG "/*verilator tag*/"
%token<fl> yVL_TRACE_INIT_TASK "/*verilator trace_init_task*/"
%token<fl> yVL_CLOCKER "/*verilator clocker*/"
%token<fl> yVL_CLOCK_ENABLE "/*verilator clock_enable*/"
%token<fl> yVL_COVERAGE_BLOCK_OFF "/*verilator coverage_block_off*/"
%token<fl> yVL_FORCEABLE "/*verilator forceable*/"
%token<fl> yVL_FULL_CASE "/*verilator full_case*/"
%token<fl> yVL_HIER_BLOCK "/*verilator hier_block*/"
%token<fl> yVL_INLINE_MODULE "/*verilator inline_module*/"
%token<fl> yVL_ISOLATE_ASSIGNMENTS "/*verilator isolate_assignments*/"
%token<fl> yVL_NO_CLOCKER "/*verilator no_clocker*/"
%token<fl> yVL_NO_INLINE_MODULE "/*verilator no_inline_module*/"
%token<fl> yVL_NO_INLINE_TASK "/*verilator no_inline_task*/"
%token<fl> yVL_PARALLEL_CASE "/*verilator parallel_case*/"
%token<fl> yVL_PUBLIC "/*verilator public*/"
%token<fl> yVL_PUBLIC_FLAT "/*verilator public_flat*/"
%token<fl> yVL_PUBLIC_FLAT_ON "/*verilator public_flat_on*/"
%token<fl> yVL_PUBLIC_FLAT_RD "/*verilator public_flat_rd*/"
%token<fl> yVL_PUBLIC_FLAT_RD_ON "/*verilator public_flat_rd_on*/"
%token<fl> yVL_PUBLIC_FLAT_RW "/*verilator public_flat_rw*/"
%token<fl> yVL_PUBLIC_FLAT_RW_ON "/*verilator public_flat_rw_on*/"
%token<fl> yVL_PUBLIC_FLAT_RW_ON_SNS "/*verilator public_flat_rw_on_sns*/"
%token<fl> yVL_PUBLIC_ON "/*verilator public_on*/"
%token<fl> yVL_PUBLIC_OFF "/*verilator public_off*/"
%token<fl> yVL_PUBLIC_MODULE "/*verilator public_module*/"
%token<fl> yVL_SC_BV "/*verilator sc_bv*/"
%token<fl> yVL_SFORMAT "/*verilator sformat*/"
%token<fl> yVL_SPLIT_VAR "/*verilator split_var*/"
%token<strp> yVL_TAG "/*verilator tag*/"
%token<fl> yVL_TRACE_INIT_TASK "/*verilator trace_init_task*/"
%token<fl> yP_TICK "'"
%token<fl> yP_TICKBRA "'{"
@ -1233,6 +1253,7 @@ package_item<nodep>: // ==IEEE: package_item
| anonymous_program { $$ = $1; }
| package_export_declaration { $$ = $1; }
| timeunits_declaration { $$ = $1; }
| sigAttrScope { $$ = nullptr; }
;
package_or_generate_item_declaration<nodep>: // ==IEEE: package_or_generate_item_declaration
@ -1344,6 +1365,7 @@ modFront<nodeModulep>:
PARSEP->rootp()->addModulesp($$);
SYMP->pushNew($$);
GRAMMARP->m_modp = $$; }
| modFront sigAttrScope { $$ = $1; }
;
importsAndParametersE<nodep>: // IEEE: common part of module_declaration, interface_declaration, program_declaration
@ -1402,13 +1424,18 @@ parameter_port_listE<nodep>: // IEEE: parameter_port_list + empty == paramete
paramPortDeclOrArgList<nodep>: // IEEE: list_of_param_assignments + { parameter_port_declaration }
paramPortDeclOrArg { $$ = $1; }
| paramPortDeclOrArgList ',' paramPortDeclOrArg { $$ = $1->addNext($3); }
| paramPortDeclOrArgList sigAttrScope {$$ = $1;}
;
paramPortDeclOrArg<nodep>: // IEEE: param_assignment + parameter_port_declaration
// // We combine the two as we can't tell which follows a comma
paramPortDeclOrArgSub {$$ = $1;}
| vlTag { $$ = nullptr; }
;
paramPortDeclOrArgSub<nodep>:
parameter_port_declarationFrontE param_assignment { $$ = $2; }
| parameter_port_declarationTypeFrontE type_assignment { $$ = $2; }
| vlTag { $$ = nullptr; }
| sigAttrScope paramPortDeclOrArgSub {$$ = $2; }
;
portsStarE<nodep>: // IEEE: .* + list_of_ports + list_of_port_declarations + empty
@ -1426,7 +1453,7 @@ list_of_portsE<nodep>: // IEEE: list_of_ports + list_of_port_declaratio
list_of_ports<nodep>: // IEEE: list_of_ports + list_of_port_declarations
portAndTag { $$ = $1; }
| list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); }
| list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); }
;
portAndTagE<nodep>:
@ -1444,11 +1471,13 @@ portAndTagE<nodep>:
$$ = $$->addNext(varp);
$$->v3warn(NULLPORT, "Null port on module (perhaps extraneous comma)"); }
| portAndTag { $$ = $1; }
| portAndTag sigAttrScope { $$ = $1; }
;
portAndTag<nodep>:
port { $$ = $1; }
| vlTag port { $$ = $2; } // Tag will associate with previous port
| sigAttrScope portAndTag { $$ = $2; }
;
port<nodep>: // ==IEEE: port
@ -1597,6 +1626,7 @@ intFront<nodeModulep>:
$$->lifetime($2);
PARSEP->rootp()->addModulesp($$);
SYMP->pushNew($$); }
| intFront sigAttrScope { $$ = $1; }
;
interface_itemListE<nodep>:
@ -2590,6 +2620,7 @@ module_common_item<nodep>: // ==IEEE: module_common_item
| loop_generate_construct { $$ = $1; }
| conditional_generate_construct { $$ = $1; }
| elaboration_system_task { $$ = $1; }
| sigAttrScope { $$ = nullptr; }
//
| error ';' { $$ = nullptr; }
;
@ -2920,7 +2951,20 @@ netId<strp>:
| idSVKwd { $$ = $1; $<fl>$ = $<fl>1; }
;
sigAttrListE<nodep>:
sigAttrScope:
yVL_PUBLIC_FLAT_RW_ON_SNS attr_event_control
{ AstNode* sigAttrsp = new AstAttrOf{$1, VAttrType::VAR_PUBLIC_FLAT_RW};
sigAttrsp->addNext(new AstAlwaysPublic{$1, $2, nullptr});
GRAMMARP->setScopedSigAttr(sigAttrsp);
v3Global.dpi(true); }
| yVL_PUBLIC_ON { GRAMMARP->createScopedSigAttr(VAttrType::VAR_PUBLIC); }
| yVL_PUBLIC_FLAT_ON { GRAMMARP->createScopedSigAttr(VAttrType::VAR_PUBLIC_FLAT); }
| yVL_PUBLIC_FLAT_RD_ON { GRAMMARP->createScopedSigAttr(VAttrType::VAR_PUBLIC_FLAT_RD); }
| yVL_PUBLIC_FLAT_RW_ON { GRAMMARP->createScopedSigAttr(VAttrType::VAR_PUBLIC_FLAT_RW); }
| yVL_PUBLIC_OFF { GRAMMARP->setScopedSigAttr(nullptr); }
;
sigAttrListE<nodep>: // Scoped Attributes are added to explicit attributes
/* empty */ { $$ = nullptr; }
| sigAttrList { $$ = $1; }
;

View File

@ -19,8 +19,14 @@
#include "verilated_vcd_c.h"
#include "verilated_vpi.h"
#ifdef T_VPI_VAR2
#include "Vt_vpi_var2.h"
#include "Vt_vpi_var2__Dpi.h"
#else
#include "Vt_vpi_var.h"
#include "Vt_vpi_var__Dpi.h"
#endif
#include "svdpi.h"
#endif
@ -267,6 +273,25 @@ int _mon_check_var() {
TestVpiHandle vh3 = vpi_handle_by_name((PLI_BYTE8*)"onebit", vh2);
CHECK_RESULT_NZ(vh3);
#ifdef T_VPI_VAR2
// test scoped attributes
TestVpiHandle vh_invisible1 = vpi_handle_by_name((PLI_BYTE8*)"invisible1", vh2);
CHECK_RESULT_Z(vh_invisible1);
TestVpiHandle vh_invisible2 = vpi_handle_by_name((PLI_BYTE8*)"invisible2", vh2);
CHECK_RESULT_Z(vh_invisible2);
TestVpiHandle vh_visibleParam1 = vpi_handle_by_name((PLI_BYTE8*)"visibleParam1", vh2);
CHECK_RESULT_NZ(vh_visibleParam1);
TestVpiHandle vh_invisibleParam1 = vpi_handle_by_name((PLI_BYTE8*)"invisibleParam1", vh2);
CHECK_RESULT_Z(vh_invisibleParam1);
TestVpiHandle vh_visibleParam2 = vpi_handle_by_name((PLI_BYTE8*)"visibleParam2", vh2);
CHECK_RESULT_NZ(vh_visibleParam2);
#endif
// onebit attributes
PLI_INT32 d;
d = vpi_get(vpiType, vh3);

31
test_regress/t/t_vpi_var2.pl Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2023 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
pli_filename("t_vpi_var.cpp");
compile(
make_top_shell => 0,
make_main => 0,
make_pli => 1,
sim_time => 2100,
iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI -DWAVES -DT_VPI_VAR2"],
v_flags2 => ["+define+USE_VPI_NOT_DPI"],
verilator_flags2 => ["--exe --vpi --no-l2name $Self->{t_dir}/t_vpi_var.cpp"],
);
execute(
use_libvpi => 1,
check_finished => 1,
all_run_flags => ['+PLUS +INT=1234 +STRSTR']
);
ok(1);
1;

158
test_regress/t/t_vpi_var2.v Normal file
View File

@ -0,0 +1,158 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Copyright 2023 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.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
`ifdef USE_VPI_NOT_DPI
//We call it via $c so we can verify DPI isn't required - see bug572
`else
import "DPI-C" context function int mon_check();
`endif
module t
/* verilator public_flat_on */
#(
parameter int visibleParam1 = 0,
/* verilator public_off */
parameter int invisibleParam1 = 1,
/* verilator public_on */
parameter int visibleParam2 = 2
/* verilator public_off */
)
(/*AUTOARG*/
// Inputs
clk
);
`ifdef VERILATOR
`systemc_header
extern "C" int mon_check();
`verilog
`endif
input clk;
/*verilator public_flat_rw_on @(posedge clk)*/
reg onebit;
reg [2:1] twoone;
reg [2:1] fourthreetwoone[4:3];
reg LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND;
// verilator lint_off LITENDIAN
reg [0:61] quads[2:3] /*verilator public_flat_rw @(posedge clk)*/;
/*verilator public_off*/
reg invisible1;
// verilator lint_on LITENDIAN
/*verilator public_flat_rd_on*/
reg [31:0] count;
reg [31:0] half_count;
/*verilator public_off*/
reg invisible2;
/*verilator public_flat_rw_on @(posedge clk)*/
reg [7:0] text_byte;
reg [15:0] text_half;
reg [31:0] text_word;
reg [63:0] text_long;
reg [511:0] text;
/*verilator public_off*/
integer status;
sub sub();
// Test loop
initial begin
count = 0;
onebit = 1'b0;
fourthreetwoone[3] = 0; // stop icarus optimizing away
text_byte = "B";
text_half = "Hf";
text_word = "Word";
text_long = "Long64b";
text = "Verilog Test module";
`ifdef VERILATOR
status = $c32("mon_check()");
`endif
`ifdef IVERILOG
status = $mon_check();
`endif
`ifndef USE_VPI_NOT_DPI
status = mon_check();
`endif
if (status!=0) begin
$write("%%Error: t_vpi_var.cpp:%0d: C Test failed\n", status);
$stop;
end
$write("%%Info: Checking results\n");
if (onebit != 1'b1) $stop;
if (quads[2] != 62'h12819213_abd31a1c) $stop;
if (quads[3] != 62'h1c77bb9b_3784ea09) $stop;
if (text_byte != "A") $stop;
if (text_half != "T2") $stop;
if (text_word != "Tree") $stop;
if (text_long != "44Four44") $stop;
if (text != "lorem ipsum") $stop;
end
always @(posedge clk) begin
count <= count + 2;
if (count[1])
half_count <= half_count + 2;
if (count == 1000) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
genvar i;
generate
for (i=1; i<=6; i=i+1) begin : arr
arr #(.LENGTH(i)) arr();
end
endgenerate
genvar k;
generate
for (k=1; k<=6; k=k+1) begin : subs
sub subsub();
end
endgenerate
endmodule : t
module sub;
reg subsig1 /*verilator public_flat_rw*/;
reg subsig2 /*verilator public_flat_rd*/;
`ifdef IVERILOG
// stop icarus optimizing signals away
wire redundant = subsig1 | subsig2;
`endif
endmodule : sub
module arr;
parameter LENGTH = 1;
/*verilator public_flat_rw_on*/
reg [LENGTH-1:0] sig;
reg [LENGTH-1:0] rfr;
reg check;
reg verbose;
/*verilator public_off*/
initial begin
sig = {LENGTH{1'b0}};
rfr = {LENGTH{1'b0}};
end
always @(posedge check) begin
if (verbose) $display("%m : %x %x", sig, rfr);
if (check && sig != rfr) $stop;
check <= 0;
end
endmodule : arr