From b2e9717419e9a852f4e64f12891b8e9742900917 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Tue, 31 Aug 2021 11:45:02 -0600 Subject: [PATCH] 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. --- frontends/verilog/verilog_parser.y | 96 +++++++++++++++++++++++++++- tests/verilog/genfor_decl_no_init.ys | 7 ++ tests/verilog/genfor_decl_no_sv.ys | 7 ++ tests/verilog/genvar_loop_decl_1.sv | 18 ++++++ tests/verilog/genvar_loop_decl_1.ys | 14 ++++ tests/verilog/genvar_loop_decl_2.sv | 30 +++++++++ tests/verilog/genvar_loop_decl_2.ys | 5 ++ tests/verilog/genvar_loop_decl_3.sv | 28 ++++++++ tests/verilog/genvar_loop_decl_3.ys | 5 ++ 9 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 tests/verilog/genfor_decl_no_init.ys create mode 100644 tests/verilog/genfor_decl_no_sv.ys create mode 100644 tests/verilog/genvar_loop_decl_1.sv create mode 100644 tests/verilog/genvar_loop_decl_1.ys create mode 100644 tests/verilog/genvar_loop_decl_2.sv create mode 100644 tests/verilog/genvar_loop_decl_2.ys create mode 100644 tests/verilog/genvar_loop_decl_3.sv create mode 100644 tests/verilog/genvar_loop_decl_3.ys diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 23404f844..8d0ba4cf6 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -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 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 attr case_attr %type struct_union %type asgn_binop +%type genvar_identifier %type specify_target %type 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 ')' { diff --git a/tests/verilog/genfor_decl_no_init.ys b/tests/verilog/genfor_decl_no_init.ys new file mode 100644 index 000000000..348899195 --- /dev/null +++ b/tests/verilog/genfor_decl_no_init.ys @@ -0,0 +1,7 @@ +logger -expect error "Generate for loop variable declaration is missing initialization!" 1 +read_verilog -sv <