sv: support for parameters without default values
authorZachary Snow <zach@zachjs.com>
Tue, 2 Mar 2021 15:43:53 +0000 (10:43 -0500)
committerZachary Snow <zach@zachjs.com>
Tue, 2 Mar 2021 15:43:53 +0000 (10:43 -0500)
- Modules with a parameter without a default value will be automatically
  deferred until the hierarchy pass
- Allows for parameters without defaults as module items, rather than
  just int the `parameter_port_list`, despite being forbidden in the LRM
- Check for parameters without defaults that haven't been overriden
- Add location info to parameter/localparam declarations

12 files changed:
frontends/ast/ast.cc
frontends/verilog/verilog_parser.y
tests/verilog/localparam_no_default_1.ys [new file with mode: 0644]
tests/verilog/localparam_no_default_2.ys [new file with mode: 0644]
tests/verilog/param_no_default.sv [new file with mode: 0644]
tests/verilog/param_no_default.ys [new file with mode: 0644]
tests/verilog/param_no_default_not_svmode.ys [new file with mode: 0644]
tests/verilog/param_no_default_unbound_1.ys [new file with mode: 0644]
tests/verilog/param_no_default_unbound_2.ys [new file with mode: 0644]
tests/verilog/param_no_default_unbound_3.ys [new file with mode: 0644]
tests/verilog/param_no_default_unbound_4.ys [new file with mode: 0644]
tests/verilog/param_no_default_unbound_5.ys [new file with mode: 0644]

index 57552d86c620b5783669b5ebf4976f7a9ccff1bd..7fb61bb34ed1c2d35121adc6637aeeb7b4de6027 100644 (file)
@@ -968,6 +968,14 @@ void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
        obj->attributes[ID::src] = ast->loc_string();
 }
 
+static bool param_has_no_default(const AstNode *param) {
+       const auto &children = param->children;
+       log_assert(param->type == AST_PARAMETER);
+       log_assert(children.size() <= 2);
+       return children.empty() ||
+               (children.size() == 1 && children[0]->type == AST_RANGE);
+}
+
 // create a new AstModule from an AST_MODULE AST node
 static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
 {
@@ -1006,6 +1014,10 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
 
        if (!defer)
        {
+               for (const AstNode *node : ast->children)
+                       if (node->type == AST_PARAMETER && param_has_no_default(node))
+                               log_file_error(node->filename, node->location.first_line, "Parameter `%s' has no default value and has not been overridden!\n", node->str.c_str());
+
                bool blackbox_module = flag_lib;
 
                if (!blackbox_module && !flag_noblackbox) {
@@ -1229,7 +1241,18 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
                        if (flag_icells && (*it)->str.compare(0, 2, "\\$") == 0)
                                (*it)->str = (*it)->str.substr(1);
 
-                       if (defer)
+                       bool defer_local = defer;
+                       if (!defer_local)
+                               for (const AstNode *node : (*it)->children)
+                                       if (node->type == AST_PARAMETER && param_has_no_default(node))
+                                       {
+                                               log("Deferring `%s' because it contains parameter(s) without defaults.\n", ast->str.c_str());
+                                               defer_local = true;
+                                               break;
+                                       }
+
+
+                       if (defer_local)
                                (*it)->str = "$abstract" + (*it)->str;
 
                        if (design->has((*it)->str)) {
@@ -1248,7 +1271,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
                                }
                        }
 
-                       design->add(process_module(*it, defer));
+                       design->add(process_module(*it, defer_local));
                        current_ast_mod = nullptr;
                }
                else if ((*it)->type == AST_PACKAGE) {
@@ -1619,6 +1642,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
                }
                continue;
        rewrite_parameter:
+               if (param_has_no_default(child))
+                       child->children.insert(child->children.begin(), nullptr);
                delete child->children.at(0);
                if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) {
                        child->children[0] = new AstNode(AST_REALVALUE);
index bcba9b76a94baef8a3c87b10f43bcc35f4fd9650..ea8cc0765d0e809074259d44e47514825963639c 100644 (file)
@@ -1462,7 +1462,26 @@ param_decl_list:
        single_param_decl | param_decl_list ',' single_param_decl;
 
 single_param_decl:
-       TOK_ID '=' expr {
+       single_param_decl_ident '=' expr {
+               AstNode *decl = ast_stack.back()->children.back();
+               log_assert(decl->type == AST_PARAMETER || decl->type == AST_LOCALPARAM);
+               delete decl->children[0];
+               decl->children[0] = $3;
+       } |
+       single_param_decl_ident {
+               AstNode *decl = ast_stack.back()->children.back();
+               if (decl->type != AST_PARAMETER) {
+                       log_assert(decl->type == AST_LOCALPARAM);
+                       frontend_verilog_yyerror("localparam initialization is missing!");
+               }
+               if (!sv_mode)
+                       frontend_verilog_yyerror("Parameter defaults can only be omitted in SystemVerilog mode!");
+               delete decl->children[0];
+               decl->children.erase(decl->children.begin());
+       };
+
+single_param_decl_ident:
+       TOK_ID {
                AstNode *node;
                if (astbuf1 == nullptr) {
                        if (!sv_mode)
@@ -1473,10 +1492,9 @@ single_param_decl:
                        node = astbuf1->clone();
                }
                node->str = *$1;
-               delete node->children[0];
-               node->children[0] = $3;
                ast_stack.back()->children.push_back(node);
                delete $1;
+               SET_AST_NODE_LOC(node, @1, @1);
        };
 
 defparam_decl:
diff --git a/tests/verilog/localparam_no_default_1.ys b/tests/verilog/localparam_no_default_1.ys
new file mode 100644 (file)
index 0000000..426a48a
--- /dev/null
@@ -0,0 +1,17 @@
+logger -expect-no-warnings
+read_verilog -sv <<EOF
+module Module #(
+    localparam X = 1
+);
+endmodule
+EOF
+
+design -reset
+
+logger -expect error "localparam initialization is missing!" 1
+read_verilog <<EOF
+module Module #(
+    localparam X
+);
+endmodule
+EOF
diff --git a/tests/verilog/localparam_no_default_2.ys b/tests/verilog/localparam_no_default_2.ys
new file mode 100644 (file)
index 0000000..b7b2622
--- /dev/null
@@ -0,0 +1,15 @@
+logger -expect-no-warnings
+read_verilog -sv <<EOF
+module Module;
+    localparam X = 1;
+endmodule
+EOF
+
+design -reset
+
+logger -expect error "localparam initialization is missing!" 1
+read_verilog <<EOF
+module Module;
+    localparam X;
+endmodule
+EOF
diff --git a/tests/verilog/param_no_default.sv b/tests/verilog/param_no_default.sv
new file mode 100644 (file)
index 0000000..cc35bd2
--- /dev/null
@@ -0,0 +1,52 @@
+module example #(
+    parameter w,
+    parameter x = 1,
+    parameter byte y,
+    parameter byte z = 3
+) (
+    output a, b,
+    output byte c, d
+);
+    assign a = w;
+    assign b = x;
+    assign c = y;
+    assign d = z;
+endmodule
+
+module top;
+    wire a1, b1;
+    wire a2, b2;
+    wire a3, b3;
+    wire a4, b4;
+    byte c1, d1;
+    byte c2, d2;
+    byte c3, d3;
+    byte c4, d4;
+
+    example #(0, 1, 2) e1(a1, b1, c1, d1);
+    example #(.w(1), .y(4)) e2(a2, b2, c2, d2);
+    example #(.x(0), .w(1), .y(5)) e3(a3, b3, c3, d3);
+    example #(1, 0, 9, 10) e4(a4, b4, c4, d4);
+
+    always @* begin
+        assert (a1 == 0);
+        assert (b1 == 1);
+        assert (c1 == 2);
+        assert (d1 == 3);
+
+        assert (a2 == 1);
+        assert (b2 == 1);
+        assert (c2 == 4);
+        assert (d3 == 3);
+
+        assert (a3 == 1);
+        assert (b3 == 0);
+        assert (c3 == 5);
+        assert (d3 == 3);
+
+        assert (a4 == 1);
+        assert (b4 == 0);
+        assert (c4 == 9);
+        assert (d4 == 10);
+    end
+endmodule
diff --git a/tests/verilog/param_no_default.ys b/tests/verilog/param_no_default.ys
new file mode 100644 (file)
index 0000000..7f161a9
--- /dev/null
@@ -0,0 +1,7 @@
+read_verilog -sv param_no_default.sv
+hierarchy
+proc
+flatten
+opt -full
+select -module top
+sat -verify -seq 1 -tempinduct -prove-asserts -show-all
diff --git a/tests/verilog/param_no_default_not_svmode.ys b/tests/verilog/param_no_default_not_svmode.ys
new file mode 100644 (file)
index 0000000..1ded84e
--- /dev/null
@@ -0,0 +1,26 @@
+logger -expect-no-warnings
+read_verilog -sv <<EOF
+module Module;
+    parameter X;
+endmodule
+EOF
+
+design -reset
+
+logger -expect-no-warnings
+read_verilog -sv <<EOF
+module Module #(
+    parameter X
+);
+endmodule
+EOF
+
+design -reset
+
+logger -expect error "Parameter defaults can only be omitted in SystemVerilog mode!" 1
+read_verilog <<EOF
+module Module #(
+    parameter X
+);
+endmodule
+EOF
diff --git a/tests/verilog/param_no_default_unbound_1.ys b/tests/verilog/param_no_default_unbound_1.ys
new file mode 100644 (file)
index 0000000..4aab85a
--- /dev/null
@@ -0,0 +1,12 @@
+read_verilog -sv <<EOF
+module Example #(
+    parameter X
+);
+endmodule
+module top;
+    Example e();
+endmodule
+EOF
+
+logger -expect error "Parameter `\\X' has no default value and has not been overridden!" 1
+hierarchy -top top
diff --git a/tests/verilog/param_no_default_unbound_2.ys b/tests/verilog/param_no_default_unbound_2.ys
new file mode 100644 (file)
index 0000000..4b7f3b0
--- /dev/null
@@ -0,0 +1,12 @@
+read_verilog -sv <<EOF
+module Example #(
+    parameter X, Y
+);
+endmodule
+module top;
+    Example e();
+endmodule
+EOF
+
+logger -expect error "Parameter `\\X' has no default value and has not been overridden!" 1
+hierarchy -top top
diff --git a/tests/verilog/param_no_default_unbound_3.ys b/tests/verilog/param_no_default_unbound_3.ys
new file mode 100644 (file)
index 0000000..f32b879
--- /dev/null
@@ -0,0 +1,12 @@
+read_verilog -sv <<EOF
+module Example #(
+    parameter X, Y
+);
+endmodule
+module top;
+    Example #(1) e();
+endmodule
+EOF
+
+logger -expect error "Parameter `\\Y' has no default value and has not been overridden!" 1
+hierarchy -top top
diff --git a/tests/verilog/param_no_default_unbound_4.ys b/tests/verilog/param_no_default_unbound_4.ys
new file mode 100644 (file)
index 0000000..3a8d69d
--- /dev/null
@@ -0,0 +1,12 @@
+read_verilog -sv <<EOF
+module Example #(
+    parameter X, Y
+);
+endmodule
+module top;
+    Example #(.Y(1)) e();
+endmodule
+EOF
+
+logger -expect error "Parameter `\\X' has no default value and has not been overridden!" 1
+hierarchy -top top
diff --git a/tests/verilog/param_no_default_unbound_5.ys b/tests/verilog/param_no_default_unbound_5.ys
new file mode 100644 (file)
index 0000000..30282eb
--- /dev/null
@@ -0,0 +1,12 @@
+read_verilog -sv <<EOF
+module Example #(
+    parameter X, Y = 2
+);
+endmodule
+module top;
+    Example #(.Y(1)) e();
+endmodule
+EOF
+
+logger -expect error "Parameter `\\X' has no default value and has not been overridden!" 1
+hierarchy -top top