sv: support declaration in generate for initialization

This is accomplished by generating a unique name for the genvar,
renaming references to the genvar only in the loop's initialization,
guard, and incrementation, and finally adding a localparam inside the
loop body with the original name so that the genvar can be shadowed as
expected.
This commit is contained in:
Zachary Snow 2021-08-31 11:45:02 -06:00 committed by Zachary Snow
parent b20bb653ce
commit b2e9717419
9 changed files with 209 additions and 1 deletions

View File

@ -254,6 +254,65 @@ static void checkLabelsMatch(const char *element, const std::string *before, con
element, before->c_str() + 1, after->c_str() + 1);
}
// This transforms a loop like
// for (genvar i = 0; i < 10; i++) begin : blk
// to
// genvar _i;
// for (_i = 0; _i < 10; _i++) begin : blk
// localparam i = _i;
// where `_i` is actually some auto-generated name.
static void rewriteGenForDeclInit(AstNode *loop)
{
// check if this generate for loop contains an inline declaration
log_assert(loop->type == AST_GENFOR);
AstNode *decl = loop->children[0];
if (decl->type == AST_ASSIGN_EQ)
return;
log_assert(decl->type == AST_GENVAR);
log_assert(loop->children.size() == 5);
// identify each component of the loop
AstNode *init = loop->children[1];
AstNode *cond = loop->children[2];
AstNode *incr = loop->children[3];
AstNode *body = loop->children[4];
log_assert(init->type == AST_ASSIGN_EQ);
log_assert(incr->type == AST_ASSIGN_EQ);
log_assert(body->type == AST_GENBLOCK);
// create a unique name for the genvar
std::string old_str = decl->str;
std::string new_str = stringf("$genfordecl$%d$%s", autoidx++, old_str.c_str());
// rename and move the genvar declaration to the containing description
decl->str = new_str;
loop->children.erase(loop->children.begin());
log_assert(current_ast_mod != nullptr);
current_ast_mod->children.push_back(decl);
// create a new localparam with old name so that the items in the loop
// can simply use the old name and shadow it as necessary
AstNode *indirect = new AstNode(AST_LOCALPARAM);
indirect->str = old_str;
AstNode *ident = new AstNode(AST_IDENTIFIER);
ident->str = new_str;
indirect->children.push_back(ident);
body->children.insert(body->children.begin(), indirect);
// only perform the renaming for the initialization, guard, and
// incrementation to enable proper shadowing of the synthetic localparam
std::function<void(AstNode*)> substitute = [&](AstNode *node) {
if (node->type == AST_IDENTIFIER && node->str == old_str)
node->str = new_str;
for (AstNode *child : node->children)
substitute(child);
};
substitute(init);
substitute(cond);
substitute(incr);
}
%}
%define api.prefix {frontend_verilog_yy}
@ -321,6 +380,7 @@ static void checkLabelsMatch(const char *element, const std::string *before, con
%type <al> attr case_attr
%type <ast> struct_union
%type <ast_node_type> asgn_binop
%type <ast> genvar_identifier
%type <specify_target_ptr> specify_target
%type <specify_triple_ptr> specify_triple specify_opt_triple
@ -2978,16 +3038,50 @@ gen_stmt_or_module_body_stmt:
free_attr($1);
};
genvar_identifier:
TOK_ID {
$$ = new AstNode(AST_IDENTIFIER);
$$->str = *$1;
delete $1;
};
genvar_initialization:
TOK_GENVAR genvar_identifier {
frontend_verilog_yyerror("Generate for loop variable declaration is missing initialization!");
} |
TOK_GENVAR genvar_identifier '=' expr {
if (!sv_mode)
frontend_verilog_yyerror("Generate for loop inline variable declaration is only supported in SystemVerilog mode!");
AstNode *node = new AstNode(AST_GENVAR);
node->is_reg = true;
node->is_signed = true;
node->range_left = 31;
node->range_right = 0;
node->str = $2->str;
node->children.push_back(checkRange(node, nullptr));
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @1, @4);
node = new AstNode(AST_ASSIGN_EQ, $2, $4);
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @1, @4);
} |
genvar_identifier '=' expr {
AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3);
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @1, @3);
};
// this production creates the obligatory if-else shift/reduce conflict
gen_stmt:
TOK_FOR '(' {
AstNode *node = new AstNode(AST_GENFOR);
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
} simple_behavioral_stmt ';' expr {
} genvar_initialization ';' expr {
ast_stack.back()->children.push_back($6);
} ';' simple_behavioral_stmt ')' gen_stmt_block {
SET_AST_NODE_LOC(ast_stack.back(), @1, @11);
rewriteGenForDeclInit(ast_stack.back());
ast_stack.pop_back();
} |
TOK_IF '(' expr ')' {

View File

@ -0,0 +1,7 @@
logger -expect error "Generate for loop variable declaration is missing initialization!" 1
read_verilog -sv <<EOT
module top;
for (genvar i; i < 10; i = i + 1)
wire x;
endmodule
EOT

View File

@ -0,0 +1,7 @@
logger -expect error "Generate for loop inline variable declaration is only supported in SystemVerilog mode!" 1
read_verilog <<EOT
module top;
for (genvar i = 1; i < 10; i = i + 1)
wire x;
endmodule
EOT

View File

@ -0,0 +1,18 @@
`default_nettype none
module gate(a);
for (genvar i = 0; i < 2; i++)
wire [i:0] x = '1;
output wire [32:0] a;
assign a = {1'b0, genblk1[0].x, 1'b0, genblk1[1].x, 1'b0};
endmodule
module gold(a);
genvar i;
for (i = 0; i < 2; i++)
wire [i:0] x = '1;
output wire [32:0] a;
assign a = {1'b0, genblk1[0].x, 1'b0, genblk1[1].x, 1'b0};
endmodule

View File

@ -0,0 +1,14 @@
read_verilog -sv genvar_loop_decl_1.sv
select -assert-count 1 gate/genblk1[0].x
select -assert-count 1 gate/genblk1[1].x
select -assert-count 0 gate/genblk1[2].x
select -assert-count 1 gold/genblk1[0].x
select -assert-count 1 gold/genblk1[1].x
select -assert-count 0 gold/genblk1[2].x
proc
equiv_make gold gate equiv
equiv_simple
equiv_status -assert

View File

@ -0,0 +1,30 @@
`default_nettype none
module gate(out);
wire [3:0] x;
for (genvar x = 0; x < 2; x++) begin : blk
localparam w = x;
if (x == 0) begin : sub
wire [w:0] x;
end
end
assign x = 2;
assign blk[0].sub.x = '1;
output wire [9:0] out;
assign out = {1'bx, x, blk[0].sub.x};
endmodule
module gold(out);
wire [3:0] x;
genvar z;
for (z = 0; z < 2; z++) begin : blk
localparam w = z;
if (z == 0) begin : sub
wire [w:0] x;
end
end
assign x = 2;
assign blk[0].sub.x = '1;
output wire [9:0] out;
assign out = {1'bx, x, blk[0].sub.x};
endmodule

View File

@ -0,0 +1,5 @@
read_verilog -sv genvar_loop_decl_2.sv
proc
equiv_make gold gate equiv
equiv_simple
equiv_status -assert

View File

@ -0,0 +1,28 @@
`default_nettype none
module gate(x, y);
output reg [15:0] x, y;
if (1) begin : gen
integer x, y;
for (genvar x = 0; x < 2; x++)
if (x == 0)
initial gen.x = 10;
assign y = x + 1;
end
initial x = gen.x;
assign y = gen.y;
endmodule
module gold(x, y);
output reg [15:0] x, y;
if (1) begin : gen
integer x, y;
genvar z;
for (z = 0; z < 2; z++)
if (z == 0)
initial x = 10;
assign y = x + 1;
end
initial x = gen.x;
assign y = gen.y;
endmodule

View File

@ -0,0 +1,5 @@
read_verilog -sv genvar_loop_decl_3.sv
proc
equiv_make gold gate equiv
equiv_simple
equiv_status -assert