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
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) {
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;
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)
{
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();