Added "via_celltype" attribute on task/func
authorClifford Wolf <clifford@clifford.at>
Mon, 18 Aug 2014 12:29:30 +0000 (14:29 +0200)
committerClifford Wolf <clifford@clifford.at>
Mon, 18 Aug 2014 12:29:30 +0000 (14:29 +0200)
README
frontends/ast/simplify.cc
frontends/verilog/parser.y

diff --git a/README b/README
index 1ecaa07df4f3be264ce16c9377eba7fc72e4b793..a0e67e8a46edbde6a709102ebf5784f9ded0f91c 100644 (file)
--- a/README
+++ b/README
@@ -290,6 +290,33 @@ Verilog Attributes and non-standard features
          assign b = 42;
       """
 
+- The attribute "via_celltype" can be used to implement a verilog task or
+  function by instantiating the specified cell type. The value is the name
+  of the cell type to use. For functions the name of the output port can
+  be specified by appending it to the cell type separated by a whitespace.
+  The body of the task or function is unused in this case and can be used
+  to specify a behavioral model of the cell type for simulation. For example:
+
+      module my_add3(A, B, C, Y);
+        parameter WIDTH = 8;
+        input [WIDTH-1:0] A, B, C;
+        output [WIDTH-1:0] Y;
+        ...
+      endmodule
+
+      module top;
+        ...
+        (* via_celltype = "my_add3 Y" *)
+        (* via_celltype_defparam_WIDTH = 32 *)
+        function [31:0] add3;
+          input [31:0] A, B, C;
+          begin
+            add3 = A + B + C;
+          end
+        endfunction
+        ...
+      endmodule
+
 - Sized constants (the syntax <size>'s?[bodh]<value>) support constant
   expressions as <size>. If the expresion is not a simple identifier, it
   must be put in parentheses. Examples: WIDTH'd42, (4+2)'b101010
index 85671213d60a9eeb7a24470a0802c0f57c0f6c91..2572fa4a93d00e14e11be1d958fdfa6b86778275 100644 (file)
@@ -1450,9 +1450,15 @@ skip_dynamic_range_lvalue_expansion:;
                                log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
                }
 
+               AstNode *decl = current_scope[str];
+
+               std::stringstream sstr;
+               sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++) << "$";
+               std::string prefix = sstr.str();
+
                bool recommend_const_eval = false;
                bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval);
-               if (in_param || recommend_const_eval || require_const_eval)
+               if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count("\\via_celltype"))
                {
                        bool all_args_const = true;
                        for (auto child : children) {
@@ -1474,11 +1480,6 @@ skip_dynamic_range_lvalue_expansion:;
                                log_error("Function %s can only be called with constant arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
                }
 
-               AstNode *decl = current_scope[str];
-               std::stringstream sstr;
-               sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++) << "$";
-               std::string prefix = sstr.str();
-
                size_t arg_count = 0;
                std::map<std::string, std::string> replace_rules;
 
@@ -1510,6 +1511,68 @@ skip_dynamic_range_lvalue_expansion:;
                        goto replace_fcall_with_id;
                }
 
+               if (decl->attributes.count("\\via_celltype"))
+               {
+                       std::string celltype = decl->attributes.at("\\via_celltype")->asAttrConst().decode_string();
+                       std::string outport = str;
+
+                       if (celltype.find(' ') != std::string::npos) {
+                               int pos = celltype.find(' ');
+                               outport = RTLIL::escape_id(celltype.substr(pos+1));
+                               celltype = RTLIL::escape_id(celltype.substr(0, pos));
+                       } else
+                               celltype = RTLIL::escape_id(celltype);
+
+                       AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE));
+                       cell->str = prefix.substr(0, SIZE(prefix)-1);
+                       cell->children[0]->str = celltype;
+
+                       for (auto attr : decl->attributes)
+                               if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0)
+                               {
+                                       AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone());
+                                       cell_arg->str = RTLIL::escape_id(attr.first.str().substr(strlen("\\via_celltype_defparam_")));
+                                       cell->children.push_back(cell_arg);
+                               }
+
+                       for (auto child : decl->children)
+                               if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))
+                               {
+                                       AstNode *wire = child->clone();
+                                       wire->str = prefix + wire->str;
+                                       wire->port_id = 0;
+                                       wire->is_input = false;
+                                       wire->is_output = false;
+                                       current_ast_mod->children.push_back(wire);
+                                       while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+                                       AstNode *wire_id = new AstNode(AST_IDENTIFIER);
+                                       wire_id->str = wire->str;
+
+                                       if ((child->is_input || child->is_output) && arg_count < children.size())
+                                       {
+                                               AstNode *arg = children[arg_count++]->clone();
+                                               AstNode *assign = child->is_input ?
+                                                               new AstNode(AST_ASSIGN_EQ, wire_id, arg) :
+                                                               new AstNode(AST_ASSIGN_EQ, arg, wire_id);
+
+                                               for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
+                                                       if (*it != current_block_child)
+                                                               continue;
+                                                       current_block->children.insert(it, assign);
+                                                       break;
+                                               }
+                                       }
+
+                                       AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id->clone());
+                                       cell_arg->str = child->str == str ? outport : child->str;
+                                       cell->children.push_back(cell_arg);
+                               }
+
+                       current_ast_mod->children.push_back(cell);
+                       goto replace_fcall_with_id;
+               }
+
                for (auto child : decl->children)
                        if (child->type == AST_WIRE)
                        {
index f619d3c2b0cba15658c6816def67a86b0e8843b3..bf9b21bb647668aa1d334ffa50a6095dba6db649 100644 (file)
@@ -407,33 +407,35 @@ module_body_stmt:
        always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property;
 
 task_func_decl:
-       TOK_TASK TOK_ID ';' {
+       attr TOK_TASK TOK_ID ';' {
                current_function_or_task = new AstNode(AST_TASK);
-               current_function_or_task->str = *$2;
+               current_function_or_task->str = *$3;
+               append_attr(current_function_or_task, $1);
                ast_stack.back()->children.push_back(current_function_or_task);
                ast_stack.push_back(current_function_or_task);
                current_function_or_task_port_id = 1;
-               delete $2;
+               delete $3;
        } task_func_body TOK_ENDTASK {
                current_function_or_task = NULL;
                ast_stack.pop_back();
        } |
-       TOK_FUNCTION opt_signed range_or_signed_int TOK_ID ';' {
+       attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID ';' {
                current_function_or_task = new AstNode(AST_FUNCTION);
-               current_function_or_task->str = *$4;
+               current_function_or_task->str = *$5;
+               append_attr(current_function_or_task, $1);
                ast_stack.back()->children.push_back(current_function_or_task);
                ast_stack.push_back(current_function_or_task);
                AstNode *outreg = new AstNode(AST_WIRE);
-               outreg->str = *$4;
-               outreg->is_signed = $2;
-               if ($3 != NULL) {
-                       outreg->children.push_back($3);
-                       outreg->is_signed = $2 || $3->is_signed;
-                       $3->is_signed = false;
+               outreg->str = *$5;
+               outreg->is_signed = $3;
+               if ($4 != NULL) {
+                       outreg->children.push_back($4);
+                       outreg->is_signed = $3 || $4->is_signed;
+                       $4->is_signed = false;
                }
                current_function_or_task->children.push_back(outreg);
                current_function_or_task_port_id = 1;
-               delete $4;
+               delete $5;
        } task_func_body TOK_ENDFUNCTION {
                current_function_or_task = NULL;
                ast_stack.pop_back();