verilog: fix width/sign detection for functions
authorZachary Snow <zach@zachjs.com>
Mon, 30 May 2022 20:45:39 +0000 (16:45 -0400)
committerZachary Snow <zach@zachjs.com>
Mon, 30 May 2022 20:45:39 +0000 (16:45 -0400)
CHANGELOG
frontends/ast/genrtlil.cc
tests/verilog/func_tern_hint.sv [new file with mode: 0644]
tests/verilog/func_tern_hint.ys [new file with mode: 0644]

index 4ee364a57c7a62756fb46e5310b2959ae59e7f03..60e53aa6cb65a25fd886af3913fe9c7831179743 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,6 +14,8 @@ Yosys 0.17 .. Yosys 0.17-dev
       the remaining cases
     - Fixed size and signedness computation for expressions containing array
       querying functions
+    - Fixed size and signedness computation of functions used in ternary
+      expressions or case item expressions
 
 Yosys 0.16 .. Yosys 0.17
 --------------------------
index a569c5ae2e4eb91a347b4e1b7a88cdf8c6837376..d81c53dfb6aed20736073859ab62164cf5cea191 100644 (file)
@@ -1095,8 +1095,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
                if (current_scope.count(str))
                {
                        // This width detection is needed for function calls which are
-                       // unelaborated, which currently only applies to calls to recursive
-                       // functions reached by unevaluated ternary branches.
+                       // unelaborated, which currently applies to calls to functions
+                       // reached via unevaluated ternary branches or used in case or case
+                       // item expressions.
                        const AstNode *func = current_scope.at(str);
                        if (func->type != AST_FUNCTION)
                                log_file_error(filename, location.first_line, "Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str());
@@ -1107,8 +1108,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
                                        break;
                                }
                        log_assert(wire && wire->type == AST_WIRE);
-                       sign_hint = wire->is_signed;
-                       width_hint = 1;
+                       sign_hint &= wire->is_signed;
+                       int result_width = 1;
                        if (!wire->children.empty())
                        {
                                log_assert(wire->children.size() == 1);
@@ -1121,10 +1122,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
                                if (left->type != AST_CONSTANT || right->type != AST_CONSTANT)
                                        log_file_error(filename, location.first_line, "Function %s has non-constant width!",
                                                        RTLIL::unescape_id(str).c_str());
-                               width_hint = abs(int(left->asInt(true) - right->asInt(true)));
+                               result_width = abs(int(left->asInt(true) - right->asInt(true)));
                                delete left;
                                delete right;
                        }
+                       width_hint = max(width_hint, result_width);
                        break;
                }
                YS_FALLTHROUGH
diff --git a/tests/verilog/func_tern_hint.sv b/tests/verilog/func_tern_hint.sv
new file mode 100644 (file)
index 0000000..3c58c99
--- /dev/null
@@ -0,0 +1,42 @@
+module top;
+    function automatic [30:0] func;
+        input integer inp;
+        func = { // self-determined context
+            (
+                inp == 0
+                ? -1 // causes whole ternary to be 32 bits
+                : func(inp - 1) // 31 bits, unsigned
+            ) >> 2};
+    endfunction
+    function automatic signed [3:0] dunk;
+        input integer inp;
+        dunk = (
+                inp == 0
+                ? 4'hF
+                // shouldn't make the ternary signed
+                : dunk(inp - 1)
+            ) == -1;
+    endfunction
+    localparam A = func(0);
+    localparam B = func(1);
+    localparam C = func(2);
+    localparam D = func(3);
+    localparam X = dunk(0);
+    localparam Y = dunk(1);
+    initial begin
+        assert(A == 31'h3F_FFFFFF);
+        assert(B == 31'h0F_FFFFFF);
+        assert(C == 31'h03_FFFFFF);
+        assert(D == 31'h00_FFFFFF);
+        assert(X == 0);
+        assert(Y == 0);
+    end
+    initial begin
+        logic x;
+        case (1'b1)
+            dunk(0): x = 0;
+            default: x = 1;
+        endcase
+        assert(x);
+    end
+endmodule
diff --git a/tests/verilog/func_tern_hint.ys b/tests/verilog/func_tern_hint.ys
new file mode 100644 (file)
index 0000000..ab8a1e0
--- /dev/null
@@ -0,0 +1,4 @@
+read_verilog -sv func_tern_hint.sv
+proc
+opt
+sat -verify -seq 1 -prove-asserts -show-all