verilog: improved support for recursive functions
authorZachary Snow <zach@zachjs.com>
Fri, 1 Jan 2021 00:23:36 +0000 (17:23 -0700)
committerZachary Snow <zach@zachjs.com>
Fri, 1 Jan 2021 01:33:59 +0000 (18:33 -0700)
frontends/ast/ast.h
frontends/ast/simplify.cc
tests/various/fib.v [new file with mode: 0644]
tests/various/fib.ys [new file with mode: 0644]

index 1b8ed22ca6e23df129ffb00f9de08e41accd6313..90739216679569362917423e57ec37a1e02f0aa1 100644 (file)
@@ -250,6 +250,7 @@ namespace AST
                // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
                // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
                bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
+               void replace_result_wire_name_in_function(const std::string &from, const std::string &to);
                AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
                void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope = true);
                void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules);
@@ -264,6 +265,7 @@ namespace AST
                // additional functionality for evaluating constant functions
                struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; };
                bool has_const_only_constructs(bool &recommend_const_eval);
+               bool has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval);
                void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall);
                AstNode *eval_const_function(AstNode *fcall);
                bool is_simple_const_expr();
index 17b4d4cdc2b33ff2102594ebf17e2beb70e602de..d4242f1e70887afd48dcd33143a088507fa8cf8a 100644 (file)
@@ -3175,6 +3175,8 @@ skip_dynamic_range_lvalue_expansion:;
 
                        if (all_args_const) {
                                AstNode *func_workspace = current_scope[str]->clone();
+                               func_workspace->str = NEW_ID.str();
+                               func_workspace->replace_result_wire_name_in_function(str, func_workspace->str);
                                newNode = func_workspace->eval_const_function(this);
                                delete func_workspace;
                                goto apply_newNode;
@@ -3714,12 +3716,12 @@ apply_newNode:
        return did_something;
 }
 
-static void replace_result_wire_name_in_function(AstNode *node, std::string &from, std::string &to)
+void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to)
 {
-       for (auto &it : node->children)
-               replace_result_wire_name_in_function(it, from, to);
-       if (node->str == from)
-               node->str = to;
+       for (AstNode *child : children)
+               child->replace_result_wire_name_in_function(from, to);
+       if (str == from && type != AST_FCALL && type != AST_TCALL)
+               str = to;
 }
 
 // replace a readmem[bh] TCALL ast node with a block of memory assignments
@@ -3912,7 +3914,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
 
                name_map[child->str] = new_name;
                if (child->type == AST_FUNCTION)
-                       replace_result_wire_name_in_function(child, child->str, new_name);
+                       child->replace_result_wire_name_in_function(child->str, new_name);
                else
                        child->str = new_name;
                current_scope[new_name] = child;
@@ -4492,15 +4494,31 @@ bool AstNode::detect_latch(const std::string &var)
 
 bool AstNode::has_const_only_constructs(bool &recommend_const_eval)
 {
+       std::set<std::string> visited;
+       return has_const_only_constructs(visited, recommend_const_eval);
+}
+
+bool AstNode::has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval)
+{
+       if (type == AST_FUNCTION || type == AST_TASK)
+       {
+               if (visited.count(str))
+               {
+                       recommend_const_eval = true;
+                       return false;
+               }
+               visited.insert(str);
+       }
+
        if (type == AST_FOR)
                recommend_const_eval = true;
        if (type == AST_WHILE || type == AST_REPEAT)
                return true;
        if (type == AST_FCALL && current_scope.count(str))
-               if (current_scope[str]->has_const_only_constructs(recommend_const_eval))
+               if (current_scope[str]->has_const_only_constructs(visited, recommend_const_eval))
                        return true;
        for (auto child : children)
-               if (child->AstNode::has_const_only_constructs(recommend_const_eval))
+               if (child->AstNode::has_const_only_constructs(visited, recommend_const_eval))
                        return true;
        return false;
 }
diff --git a/tests/various/fib.v b/tests/various/fib.v
new file mode 100644 (file)
index 0000000..9867496
--- /dev/null
@@ -0,0 +1,65 @@
+module gate(
+    off, fib0, fib1, fib2, fib3, fib4, fib5, fib6, fib7, fib8, fib9
+);
+    input wire signed [31:0] off;
+
+    function automatic integer fib(
+        input integer k
+    );
+        if (k == 0)
+            fib = 0;
+        else if (k == 1)
+            fib = 1;
+        else
+            fib = fib(k - 1) + fib(k - 2);
+    endfunction
+
+    function automatic integer fib_wrap(
+        input integer k,
+        output integer o
+    );
+        o = off + fib(k);
+    endfunction
+
+    output integer fib0;
+    output integer fib1;
+    output integer fib2;
+    output integer fib3;
+    output integer fib4;
+    output integer fib5;
+    output integer fib6;
+    output integer fib7;
+    output integer fib8;
+    output integer fib9;
+
+    initial begin : blk
+        integer unused;
+        unused = fib_wrap(0, fib0);
+        unused = fib_wrap(1, fib1);
+        unused = fib_wrap(2, fib2);
+        unused = fib_wrap(3, fib3);
+        unused = fib_wrap(4, fib4);
+        unused = fib_wrap(5, fib5);
+        unused = fib_wrap(6, fib6);
+        unused = fib_wrap(7, fib7);
+        unused = fib_wrap(8, fib8);
+        unused = fib_wrap(9, fib9);
+    end
+endmodule
+
+module gold(
+    off, fib0, fib1, fib2, fib3, fib4, fib5, fib6, fib7, fib8, fib9
+);
+    input wire signed [31:0] off;
+
+    output integer fib0 = off + 0;
+    output integer fib1 = off + 1;
+    output integer fib2 = off + 1;
+    output integer fib3 = off + 2;
+    output integer fib4 = off + 3;
+    output integer fib5 = off + 5;
+    output integer fib6 = off + 8;
+    output integer fib7 = off + 13;
+    output integer fib8 = off + 21;
+    output integer fib9 = off + 34;
+endmodule
diff --git a/tests/various/fib.ys b/tests/various/fib.ys
new file mode 100644 (file)
index 0000000..946e073
--- /dev/null
@@ -0,0 +1,6 @@
+read_verilog fib.v
+hierarchy
+proc
+equiv_make gold gate equiv
+equiv_simple
+equiv_status -assert