sv: support declaration in generate for initialization
authorZachary Snow <zach@zachjs.com>
Tue, 31 Aug 2021 17:45:02 +0000 (11:45 -0600)
committerZachary Snow <zachary.j.snow@gmail.com>
Tue, 31 Aug 2021 18:34:55 +0000 (12:34 -0600)
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
tests/verilog/genfor_decl_no_init.ys [new file with mode: 0644]
tests/verilog/genfor_decl_no_sv.ys [new file with mode: 0644]
tests/verilog/genvar_loop_decl_1.sv [new file with mode: 0644]
tests/verilog/genvar_loop_decl_1.ys [new file with mode: 0644]
tests/verilog/genvar_loop_decl_2.sv [new file with mode: 0644]
tests/verilog/genvar_loop_decl_2.ys [new file with mode: 0644]
tests/verilog/genvar_loop_decl_3.sv [new file with mode: 0644]
tests/verilog/genvar_loop_decl_3.ys [new file with mode: 0644]

index 23404f844b2d7c1d97cc7b925360321971f22814..8d0ba4cf67a9dd1e2f7d751df8375ce63c76df59 100644 (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 ')' {
diff --git a/tests/verilog/genfor_decl_no_init.ys b/tests/verilog/genfor_decl_no_init.ys
new file mode 100644 (file)
index 0000000..3488991
--- /dev/null
@@ -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
diff --git a/tests/verilog/genfor_decl_no_sv.ys b/tests/verilog/genfor_decl_no_sv.ys
new file mode 100644 (file)
index 0000000..124a27c
--- /dev/null
@@ -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
diff --git a/tests/verilog/genvar_loop_decl_1.sv b/tests/verilog/genvar_loop_decl_1.sv
new file mode 100644 (file)
index 0000000..b503f75
--- /dev/null
@@ -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
diff --git a/tests/verilog/genvar_loop_decl_1.ys b/tests/verilog/genvar_loop_decl_1.ys
new file mode 100644 (file)
index 0000000..ded4862
--- /dev/null
@@ -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
diff --git a/tests/verilog/genvar_loop_decl_2.sv b/tests/verilog/genvar_loop_decl_2.sv
new file mode 100644 (file)
index 0000000..c5a85ef
--- /dev/null
@@ -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
diff --git a/tests/verilog/genvar_loop_decl_2.ys b/tests/verilog/genvar_loop_decl_2.ys
new file mode 100644 (file)
index 0000000..52fdeb4
--- /dev/null
@@ -0,0 +1,5 @@
+read_verilog -sv genvar_loop_decl_2.sv
+proc
+equiv_make gold gate equiv
+equiv_simple
+equiv_status -assert
diff --git a/tests/verilog/genvar_loop_decl_3.sv b/tests/verilog/genvar_loop_decl_3.sv
new file mode 100644 (file)
index 0000000..4d6d236
--- /dev/null
@@ -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
diff --git a/tests/verilog/genvar_loop_decl_3.ys b/tests/verilog/genvar_loop_decl_3.ys
new file mode 100644 (file)
index 0000000..19f7541
--- /dev/null
@@ -0,0 +1,5 @@
+read_verilog -sv genvar_loop_decl_3.sv
+proc
+equiv_make gold gate equiv
+equiv_simple
+equiv_status -assert