/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * --- * * This is the AST frontend library. * * The AST frontend library is not a frontend on it's own but provides a * generic abstract syntax tree (AST) abstraction for HDL code and can be * used by HDL frontends. See "ast.h" for an overview of the API and the * Verilog frontend for an usage example. * */#include"kernel/log.h"#include"libs/sha1/sha1.h"#include"frontends/verilog/verilog_frontend.h"#include"ast.h"#include<sstream>#include<stdarg.h>#include<stdlib.h>#include<math.h>YOSYS_NAMESPACE_BEGINusingnamespaceAST;usingnamespaceAST_INTERNAL;// convert the AST into a simpler AST that has all parameters substituted by their// values, unrolled for-loops, expanded generate blocks, etc. when this function// is done with an AST it can be converted into RTLIL using genRTLIL().//// this function also does all name resolving and sets the id2ast member of all// nodes that link to a different node using names and lexical scoping.boolAstNode::simplify(boolconst_fold,boolat_zero,boolin_lvalue,intstage,intwidth_hint,boolsign_hint,boolin_param){staticintrecursion_counter=0;staticbooldeep_recursion_warning=false;if(recursion_counter++==1000&&deep_recursion_warning){log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n");deep_recursion_warning=false;}AstNode*newNode=NULL;booldid_something=false;#if 0 log("-------------\n"); log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), linenum, type2str(type).c_str(), this); log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); // dumpAst(NULL, "> ");#endifif(stage==0){log_assert(type==AST_MODULE||type==AST_INTERFACE);deep_recursion_warning=true;while(simplify(const_fold,at_zero,in_lvalue,1,width_hint,sign_hint,in_param)){}if(!flag_nomem2reg&&!get_bool_attribute("\\nomem2reg")){dict<AstNode*,pool<std::string>>mem2reg_places;dict<AstNode*,uint32_t>mem2reg_candidates,dummy_proc_flags;uint32_tflags=flag_mem2reg?AstNode::MEM2REG_FL_ALL:0;mem2reg_as_needed_pass1(mem2reg_places,mem2reg_candidates,dummy_proc_flags,flags);pool<AstNode*>mem2reg_set;for(auto&it:mem2reg_candidates){AstNode*mem=it.first;uint32_tmemflags=it.second;boolthis_nomeminit=flag_nomeminit;log_assert((memflags&~0x00ffff00)==0);if(mem->get_bool_attribute("\\nomem2reg"))continue;if(mem->get_bool_attribute("\\nomeminit")||get_bool_attribute("\\nomeminit"))this_nomeminit=true;if(memflags&AstNode::MEM2REG_FL_FORCED)gotosilent_activate;if(memflags&AstNode::MEM2REG_FL_EQ2)gotoverbose_activate;if(memflags&AstNode::MEM2REG_FL_SET_ASYNC)gotoverbose_activate;if((memflags&AstNode::MEM2REG_FL_SET_INIT)&&(memflags&AstNode::MEM2REG_FL_SET_ELSE)&&this_nomeminit)gotoverbose_activate;if(memflags&AstNode::MEM2REG_FL_CMPLX_LHS)gotoverbose_activate;if((memflags&AstNode::MEM2REG_FL_CONST_LHS)&&!(memflags&AstNode::MEM2REG_FL_VAR_LHS))gotoverbose_activate;// log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));continue;verbose_activate:if(mem2reg_set.count(mem)==0){std::stringmessage=stringf("Replacing memory %s with list of registers.",mem->str.c_str());boolfirst_element=true;for(auto&place:mem2reg_places[it.first]){message+=stringf("%s%s",first_element?" See ":", ",place.c_str());first_element=false;}log_warning("%s\n",message.c_str());}silent_activate:// log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));mem2reg_set.insert(mem);}for(autonode:mem2reg_set){intmem_width,mem_size,addr_bits;node->meminfo(mem_width,mem_size,addr_bits);intdata_range_left=node->children[0]->range_left;intdata_range_right=node->children[0]->range_right;if(node->children[0]->range_swapped)std::swap(data_range_left,data_range_right);for(inti=0;i<mem_size;i++){AstNode*reg=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(data_range_left,true),mkconst_int(data_range_right,true)));reg->str=stringf("%s[%d]",node->str.c_str(),i);reg->is_reg=true;reg->is_signed=node->is_signed;for(auto&it:node->attributes)if(it.first!=ID(mem2reg))reg->attributes.emplace(it.first,it.second->clone());reg->filename=node->filename;reg->linenum=node->linenum;children.push_back(reg);while(reg->simplify(true,false,false,1,-1,false,false)){}}}AstNode*async_block=NULL;while(mem2reg_as_needed_pass2(mem2reg_set,this,NULL,async_block)){}vector<AstNode*>delnodes;mem2reg_remove(mem2reg_set,delnodes);for(autonode:delnodes)deletenode;}while(simplify(const_fold,at_zero,in_lvalue,2,width_hint,sign_hint,in_param)){}recursion_counter--;returnfalse;}current_filename=filename;set_line_num(linenum);// we do not look inside a task or function// (but as soon as a task or function is instantiated we process the generated AST as usual)if(type==AST_FUNCTION||type==AST_TASK){recursion_counter--;returnfalse;}// deactivate all calls to non-synthesis system tasks// note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this listif((type==AST_FCALL||type==AST_TCALL)&&(str=="$strobe"||str=="$monitor"||str=="$time"||str=="$dumpfile"||str=="$dumpvars"||str=="$dumpon"||str=="$dumpoff"||str=="$dumpall")){log_file_warning(filename,linenum,"Ignoring call to system %s %s.\n",type==AST_FCALL?"function":"task",str.c_str());delete_children();str=std::string();}if((type==AST_TCALL)&&(str=="$display"||str=="$write")&&(!current_always||current_always->type!=AST_INITIAL)){log_file_warning(filename,linenum,"System task `%s' outside initial block is unsupported.\n",str.c_str());delete_children();str=std::string();}// print messages if this a call to $display() or $write()// This code implements only a small subset of Verilog-2005 $display() format specifiers,// but should be good enough for most usesif((type==AST_TCALL)&&((str=="$display")||(str=="$write"))){intnargs=GetSize(children);if(nargs<1)log_file_error(filename,linenum,"System task `%s' got %d arguments, expected >= 1.\n",str.c_str(),int(children.size()));// First argument is the format stringAstNode*node_string=children[0];while(node_string->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(node_string->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system task `%s' with non-constant 1st argument.\n",str.c_str());std::stringsformat=node_string->bitsAsConst().decode_string();// Other arguments are placeholders. Process the string as we go through itstd::stringsout;intnext_arg=1;for(size_ti=0;i<sformat.length();i++){// format specifierif(sformat[i]=='%'){// If there's no next character, that's a problemif(i+1>=sformat.length())log_file_error(filename,linenum,"System task `%s' called with `%%' at end of string.\n",str.c_str());charcformat=sformat[++i];// %% is special, does not need a matching argumentif(cformat=='%'){sout+='%';continue;}// Simplify the argumentAstNode*node_arg=nullptr;// Everything from here on depends on the format specifierswitch(cformat){case's':case'S':case'd':case'D':case'x':case'X':if(next_arg>=GetSize(children))log_file_error(filename,linenum,"Missing argument for %%%c format specifier in system task `%s'.\n",cformat,str.c_str());node_arg=children[next_arg++];while(node_arg->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(node_arg->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system task `%s' with non-constant argument.\n",str.c_str());break;case'm':case'M':break;default:log_file_error(filename,linenum,"System task `%s' called with invalid/unsupported format specifier.\n",str.c_str());break;}switch(cformat){case's':case'S':sout+=node_arg->bitsAsConst().decode_string();break;case'd':case'D':{chartmp[128];snprintf(tmp,sizeof(tmp),"%d",node_arg->bitsAsConst().as_int());sout+=tmp;}break;case'x':case'X':{chartmp[128];snprintf(tmp,sizeof(tmp),"%x",node_arg->bitsAsConst().as_int());sout+=tmp;}break;case'm':case'M':sout+=log_id(current_module->name);break;default:log_abort();}}// not a format specifierelsesout+=sformat[i];}// Finally, print the message (only include a \n for $display, not for $write)log("%s",sout.c_str());if(str=="$display")log("\n");delete_children();str=std::string();}// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)if(type==AST_WIRE||type==AST_PARAMETER||type==AST_LOCALPARAM||type==AST_DEFPARAM||type==AST_PARASET||type==AST_RANGE||type==AST_PREFIX)const_fold=true;if(type==AST_IDENTIFIER&¤t_scope.count(str)>0&&(current_scope[str]->type==AST_PARAMETER||current_scope[str]->type==AST_LOCALPARAM))const_fold=true;// in certain cases a function must be evaluated constant. this is what in_param controls.if(type==AST_PARAMETER||type==AST_LOCALPARAM||type==AST_DEFPARAM||type==AST_PARASET||type==AST_PREFIX)in_param=true;std::map<std::string,AstNode*>backup_scope;// create name resolution entries for all objects with names// also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")if(type==AST_MODULE){current_scope.clear();std::map<std::string,AstNode*>this_wire_scope;for(size_ti=0;i<children.size();i++){AstNode*node=children[i];if(node->type==AST_WIRE){if(node->children.size()==1&&node->children[0]->type==AST_RANGE){for(autoc:node->children[0]->children){if(!c->is_simple_const_expr()){if(attributes.count("\\dynports"))deleteattributes.at("\\dynports");attributes["\\dynports"]=AstNode::mkconst_int(1,true);}}}if(this_wire_scope.count(node->str)>0){AstNode*first_node=this_wire_scope[node->str];if(first_node->is_input&&node->is_reg)gotowires_are_incompatible;if(!node->is_input&&!node->is_output&&node->is_reg&&node->children.size()==0)gotowires_are_compatible;if(first_node->children.size()==0&&node->children.size()==1&&node->children[0]->type==AST_RANGE){AstNode*r=node->children[0];if(r->range_valid&&r->range_left==0&&r->range_right==0){deleter;node->children.pop_back();}}if(first_node->children.size()!=node->children.size())gotowires_are_incompatible;for(size_tj=0;j<node->children.size();j++){AstNode*n1=first_node->children[j],*n2=node->children[j];if(n1->type==AST_RANGE&&n2->type==AST_RANGE&&n1->range_valid&&n2->range_valid){if(n1->range_left!=n2->range_left)gotowires_are_incompatible;if(n1->range_right!=n2->range_right)gotowires_are_incompatible;}elseif(*n1!=*n2)gotowires_are_incompatible;}if(first_node->range_left!=node->range_left)gotowires_are_incompatible;if(first_node->range_right!=node->range_right)gotowires_are_incompatible;if(first_node->port_id==0&&(node->is_input||node->is_output))gotowires_are_incompatible;wires_are_compatible:if(node->is_input)first_node->is_input=true;if(node->is_output)first_node->is_output=true;if(node->is_reg)first_node->is_reg=true;if(node->is_logic)first_node->is_logic=true;if(node->is_signed)first_node->is_signed=true;for(auto&it:node->attributes){if(first_node->attributes.count(it.first)>0)deletefirst_node->attributes[it.first];first_node->attributes[it.first]=it.second->clone();}children.erase(children.begin()+(i--));did_something=true;deletenode;continue;wires_are_incompatible:if(stage>1)log_file_error(filename,linenum,"Incompatible re-declaration of wire %s.\n",node->str.c_str());continue;}this_wire_scope[node->str]=node;}if(node->type==AST_PARAMETER||node->type==AST_LOCALPARAM||node->type==AST_WIRE||node->type==AST_AUTOWIRE||node->type==AST_GENVAR||node->type==AST_MEMORY||node->type==AST_FUNCTION||node->type==AST_TASK||node->type==AST_DPI_FUNCTION||node->type==AST_CELL){backup_scope[node->str]=current_scope[node->str];current_scope[node->str]=node;}}for(size_ti=0;i<children.size();i++){AstNode*node=children[i];if(node->type==AST_PARAMETER||node->type==AST_LOCALPARAM||node->type==AST_WIRE||node->type==AST_AUTOWIRE||node->type==AST_MEMORY)while(node->simplify(true,false,false,1,-1,false,node->type==AST_PARAMETER||node->type==AST_LOCALPARAM))did_something=true;}}autobackup_current_block=current_block;autobackup_current_block_child=current_block_child;autobackup_current_top_block=current_top_block;autobackup_current_always=current_always;autobackup_current_always_clocked=current_always_clocked;if(type==AST_ALWAYS||type==AST_INITIAL){if(current_always!=nullptr)log_file_error(filename,linenum,"Invalid nesting of always blocks and/or initializations.\n");current_always=this;current_always_clocked=false;if(type==AST_ALWAYS)for(autochild:children){if(child->type==AST_POSEDGE||child->type==AST_NEGEDGE)current_always_clocked=true;if(child->type==AST_EDGE&&GetSize(child->children)==1&&child->children[0]->type==AST_IDENTIFIER&&child->children[0]->str=="\\$global_clock")current_always_clocked=true;}}intbackup_width_hint=width_hint;boolbackup_sign_hint=sign_hint;booldetect_width_simple=false;boolchild_0_is_self_determined=false;boolchild_1_is_self_determined=false;boolchild_2_is_self_determined=false;boolchildren_are_self_determined=false;boolreset_width_after_children=false;switch(type){caseAST_ASSIGN_EQ:caseAST_ASSIGN_LE:caseAST_ASSIGN:while(!children[0]->basic_prep&&children[0]->simplify(false,false,true,stage,-1,false,in_param)==true)did_something=true;while(!children[1]->basic_prep&&children[1]->simplify(false,false,false,stage,-1,false,in_param)==true)did_something=true;children[0]->detectSignWidth(backup_width_hint,backup_sign_hint);children[1]->detectSignWidth(width_hint,sign_hint);width_hint=max(width_hint,backup_width_hint);child_0_is_self_determined=true;// test only once, before optimizations and memory mappings but after assignment LHS was mapped to an identifierif(children[0]->id2ast&&!children[0]->was_checked){if((type==AST_ASSIGN_LE||type==AST_ASSIGN_EQ)&&children[0]->id2ast->is_logic)children[0]->id2ast->is_reg=true;// if logic type is used in a block asignmentif((type==AST_ASSIGN_LE||type==AST_ASSIGN_EQ)&&!children[0]->id2ast->is_reg)log_warning("wire '%s' is assigned in a block at %s:%d.\n",children[0]->str.c_str(),filename.c_str(),linenum);if(type==AST_ASSIGN&&children[0]->id2ast->is_reg){boolis_rand_reg=false;if(children[1]->type==AST_FCALL){if(children[1]->str=="\\$anyconst")is_rand_reg=true;if(children[1]->str=="\\$anyseq")is_rand_reg=true;if(children[1]->str=="\\$allconst")is_rand_reg=true;if(children[1]->str=="\\$allseq")is_rand_reg=true;}if(!is_rand_reg)log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n",children[0]->str.c_str(),filename.c_str(),linenum);}children[0]->was_checked=true;}break;caseAST_PARAMETER:caseAST_LOCALPARAM:while(!children[0]->basic_prep&&children[0]->simplify(false,false,false,stage,-1,false,true)==true)did_something=true;children[0]->detectSignWidth(width_hint,sign_hint);if(children.size()>1&&children[1]->type==AST_RANGE){while(!children[1]->basic_prep&&children[1]->simplify(false,false,false,stage,-1,false,true)==true)did_something=true;if(!children[1]->range_valid)log_file_error(filename,linenum,"Non-constant width range on parameter decl.\n");width_hint=max(width_hint,children[1]->range_left-children[1]->range_right+1);}break;caseAST_TO_BITS:caseAST_TO_SIGNED:caseAST_TO_UNSIGNED:caseAST_CONCAT:caseAST_REPLICATE:caseAST_REDUCE_AND:caseAST_REDUCE_OR:caseAST_REDUCE_XOR:caseAST_REDUCE_XNOR:caseAST_REDUCE_BOOL:detect_width_simple=true;children_are_self_determined=true;break;caseAST_NEG:caseAST_BIT_NOT:caseAST_POS:caseAST_BIT_AND:caseAST_BIT_OR:caseAST_BIT_XOR:caseAST_BIT_XNOR:caseAST_ADD:caseAST_SUB:caseAST_MUL:caseAST_DIV:caseAST_MOD:detect_width_simple=true;break;caseAST_SHIFT_LEFT:caseAST_SHIFT_RIGHT:caseAST_SHIFT_SLEFT:caseAST_SHIFT_SRIGHT:caseAST_POW:detect_width_simple=true;child_1_is_self_determined=true;break;caseAST_LT:caseAST_LE:caseAST_EQ:caseAST_NE:caseAST_EQX:caseAST_NEX:caseAST_GE:caseAST_GT:width_hint=-1;sign_hint=true;for(autochild:children){while(!child->basic_prep&&child->simplify(false,false,in_lvalue,stage,-1,false,in_param)==true)did_something=true;child->detectSignWidthWorker(width_hint,sign_hint);}reset_width_after_children=true;break;caseAST_LOGIC_AND:caseAST_LOGIC_OR:caseAST_LOGIC_NOT:detect_width_simple=true;children_are_self_determined=true;break;caseAST_TERNARY:detect_width_simple=true;child_0_is_self_determined=true;break;caseAST_MEMRD:detect_width_simple=true;children_are_self_determined=true;break;caseAST_FCALL:caseAST_TCALL:children_are_self_determined=true;break;default:width_hint=-1;sign_hint=false;}if(detect_width_simple&&width_hint<0){if(type==AST_REPLICATE)while(children[0]->simplify(true,false,in_lvalue,stage,-1,false,true)==true)did_something=true;for(autochild:children)while(!child->basic_prep&&child->simplify(false,false,in_lvalue,stage,-1,false,in_param)==true)did_something=true;detectSignWidth(width_hint,sign_hint);}if(type==AST_FCALL&&str=="\\$past")detectSignWidth(width_hint,sign_hint);if(type==AST_TERNARY){intwidth_hint_left,width_hint_right;boolsign_hint_left,sign_hint_right;boolfound_real_left,found_real_right;children[1]->detectSignWidth(width_hint_left,sign_hint_left,&found_real_left);children[2]->detectSignWidth(width_hint_right,sign_hint_right,&found_real_right);if(found_real_left||found_real_right){child_1_is_self_determined=true;child_2_is_self_determined=true;}}if(type==AST_CONDX&&children.size()>0&&children.at(0)->type==AST_CONSTANT){for(auto&bit:children.at(0)->bits)if(bit==State::Sz||bit==State::Sx)bit=State::Sa;}if(type==AST_CONDZ&&children.size()>0&&children.at(0)->type==AST_CONSTANT){for(auto&bit:children.at(0)->bits)if(bit==State::Sz)bit=State::Sa;}if(const_fold&&type==AST_CASE){while(children[0]->simplify(const_fold,at_zero,in_lvalue,stage,width_hint,sign_hint,in_param)){}if(children[0]->type==AST_CONSTANT&&children[0]->bits_only_01()){std::vector<AstNode*>new_children;new_children.push_back(children[0]);for(inti=1;i<GetSize(children);i++){AstNode*child=children[i];log_assert(child->type==AST_COND||child->type==AST_CONDX||child->type==AST_CONDZ);for(autov:child->children){if(v->type==AST_DEFAULT)gotokeep_const_cond;if(v->type==AST_BLOCK)continue;while(v->simplify(const_fold,at_zero,in_lvalue,stage,width_hint,sign_hint,in_param)){}if(v->type==AST_CONSTANT&&v->bits_only_01()){if(v->bits==children[0]->bits){while(i+1<GetSize(children))deletechildren[++i];gotokeep_const_cond;}continue;}gotokeep_const_cond;}if(0)keep_const_cond:new_children.push_back(child);elsedeletechild;}new_children.swap(children);}}// simplify all children first// (iterate by index as e.g. auto wires can add new children in the process)for(size_ti=0;i<children.size();i++){booldid_something_here=true;boolbackup_flag_autowire=flag_autowire;if((type==AST_GENFOR||type==AST_FOR)&&i>=3)break;if((type==AST_GENIF||type==AST_GENCASE)&&i>=1)break;if(type==AST_GENBLOCK)break;if(type==AST_BLOCK&&!str.empty())break;if(type==AST_PREFIX&&i>=1)break;if(type==AST_DEFPARAM&&i==0)flag_autowire=true;while(did_something_here&&i<children.size()){boolconst_fold_here=const_fold,in_lvalue_here=in_lvalue;intwidth_hint_here=width_hint;boolsign_hint_here=sign_hint;boolin_param_here=in_param;if(i==0&&(type==AST_REPLICATE||type==AST_WIRE))const_fold_here=true,in_param_here=true;if(type==AST_PARAMETER||type==AST_LOCALPARAM)const_fold_here=true;if(i==0&&(type==AST_ASSIGN||type==AST_ASSIGN_EQ||type==AST_ASSIGN_LE))in_lvalue_here=true;if(type==AST_BLOCK){current_block=this;current_block_child=children[i];}if((type==AST_ALWAYS||type==AST_INITIAL)&&children[i]->type==AST_BLOCK)current_top_block=children[i];if(i==0&&child_0_is_self_determined)width_hint_here=-1,sign_hint_here=false;if(i==1&&child_1_is_self_determined)width_hint_here=-1,sign_hint_here=false;if(i==2&&child_2_is_self_determined)width_hint_here=-1,sign_hint_here=false;if(children_are_self_determined)width_hint_here=-1,sign_hint_here=false;did_something_here=children[i]->simplify(const_fold_here,at_zero,in_lvalue_here,stage,width_hint_here,sign_hint_here,in_param_here);if(did_something_here)did_something=true;}if(stage==2&&children[i]->type==AST_INITIAL&¤t_ast_mod!=this){current_ast_mod->children.push_back(children[i]);children.erase(children.begin()+(i--));did_something=true;}flag_autowire=backup_flag_autowire;}for(auto&attr:attributes){while(attr.second->simplify(true,false,false,stage,-1,false,true))did_something=true;}if(reset_width_after_children){width_hint=backup_width_hint;sign_hint=backup_sign_hint;if(width_hint<0)detectSignWidth(width_hint,sign_hint);}current_block=backup_current_block;current_block_child=backup_current_block_child;current_top_block=backup_current_top_block;current_always=backup_current_always;current_always_clocked=backup_current_always_clocked;for(autoit=backup_scope.begin();it!=backup_scope.end();it++){if(it->second==NULL)current_scope.erase(it->first);elsecurrent_scope[it->first]=it->second;}current_filename=filename;set_line_num(linenum);if(type==AST_MODULE)current_scope.clear();// convert defparam nodes to cell parametersif(type==AST_DEFPARAM&&!children.empty()){if(children[0]->type!=AST_IDENTIFIER)log_file_error(filename,linenum,"Module name in defparam contains non-constant expressions!\n");stringmodname,paramname=children[0]->str;size_tpos=paramname.rfind('.');while(pos!=0&&pos!=std::string::npos){modname=paramname.substr(0,pos);if(current_scope.count(modname))break;pos=paramname.rfind('.',pos-1);}if(pos==std::string::npos)log_file_error(filename,linenum,"Can't find object for defparam `%s`!\n",RTLIL::unescape_id(paramname).c_str());paramname="\\"+paramname.substr(pos+1);if(current_scope.at(modname)->type!=AST_CELL)log_file_error(filename,linenum,"Defparam argument `%s . %s` does not match a cell!\n",RTLIL::unescape_id(modname).c_str(),RTLIL::unescape_id(paramname).c_str());AstNode*paraset=newAstNode(AST_PARASET,children[1]->clone(),GetSize(children)>2?children[2]->clone():NULL);paraset->str=paramname;AstNode*cell=current_scope.at(modname);cell->children.insert(cell->children.begin()+1,paraset);delete_children();}// resolve constant prefixesif(type==AST_PREFIX){if(children[0]->type!=AST_CONSTANT){// dumpAst(NULL, "> ");log_file_error(filename,linenum,"Index in generate block prefix syntax is not constant!\n");}if(children[1]->type==AST_PREFIX)children[1]->simplify(const_fold,at_zero,in_lvalue,stage,width_hint,sign_hint,in_param);log_assert(children[1]->type==AST_IDENTIFIER);newNode=children[1]->clone();constchar*second_part=children[1]->str.c_str();if(second_part[0]=='\\')second_part++;newNode->str=stringf("%s[%d].%s",str.c_str(),children[0]->integer,second_part);gotoapply_newNode;}// evaluate TO_BITS nodesif(type==AST_TO_BITS){if(children[0]->type!=AST_CONSTANT)log_file_error(filename,linenum,"Left operand of to_bits expression is not constant!\n");if(children[1]->type!=AST_CONSTANT)log_file_error(filename,linenum,"Right operand of to_bits expression is not constant!\n");RTLIL::Constnew_value=children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(),children[1]->is_signed);newNode=mkconst_bits(new_value.bits,children[1]->is_signed);gotoapply_newNode;}// annotate constant rangesif(type==AST_RANGE){boolold_range_valid=range_valid;range_valid=false;range_swapped=false;range_left=-1;range_right=0;log_assert(children.size()>=1);if(children[0]->type==AST_CONSTANT){range_valid=true;range_left=children[0]->integer;if(children.size()==1)range_right=range_left;}if(children.size()>=2){if(children[1]->type==AST_CONSTANT)range_right=children[1]->integer;elserange_valid=false;}if(old_range_valid!=range_valid)did_something=true;if(range_valid&&range_left>=0&&range_right>range_left){inttmp=range_right;range_right=range_left;range_left=tmp;range_swapped=true;}}// annotate wires with their rangesif(type==AST_WIRE){if(children.size()>0){if(children[0]->range_valid){if(!range_valid)did_something=true;range_valid=true;range_swapped=children[0]->range_swapped;range_left=children[0]->range_left;range_right=children[0]->range_right;}}else{if(!range_valid)did_something=true;range_valid=true;range_swapped=false;range_left=0;range_right=0;}}// resolve multiranges on memory declif(type==AST_MEMORY&&children.size()>1&&children[1]->type==AST_MULTIRANGE){inttotal_size=1;multirange_dimensions.clear();for(autorange:children[1]->children){if(!range->range_valid)log_file_error(filename,linenum,"Non-constant range on memory decl.\n");multirange_dimensions.push_back(min(range->range_left,range->range_right));multirange_dimensions.push_back(max(range->range_left,range->range_right)-min(range->range_left,range->range_right)+1);total_size*=multirange_dimensions.back();}deletechildren[1];children[1]=newAstNode(AST_RANGE,AstNode::mkconst_int(0,true),AstNode::mkconst_int(total_size-1,true));did_something=true;}// resolve multiranges on memory accessif(type==AST_IDENTIFIER&&id2ast&&id2ast->type==AST_MEMORY&&children.size()>0&&children[0]->type==AST_MULTIRANGE){AstNode*index_expr=nullptr;for(inti=0;2*i<GetSize(id2ast->multirange_dimensions);i++){if(GetSize(children[0]->children)<i)log_file_error(filename,linenum,"Insufficient number of array indices for %s.\n",log_id(str));AstNode*new_index_expr=children[0]->children[i]->children.at(0)->clone();if(id2ast->multirange_dimensions[2*i])new_index_expr=newAstNode(AST_SUB,new_index_expr,AstNode::mkconst_int(id2ast->multirange_dimensions[2*i],true));if(i==0)index_expr=new_index_expr;elseindex_expr=newAstNode(AST_ADD,newAstNode(AST_MUL,index_expr,AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1],true)),new_index_expr);}for(inti=GetSize(id2ast->multirange_dimensions)/2;i<GetSize(children[0]->children);i++)children.push_back(children[0]->children[i]->clone());deletechildren[0];if(index_expr==nullptr)children.erase(children.begin());elsechildren[0]=newAstNode(AST_RANGE,index_expr);did_something=true;}// trim/extend parametersif(type==AST_PARAMETER||type==AST_LOCALPARAM){if(children.size()>1&&children[1]->type==AST_RANGE){if(!children[1]->range_valid)log_file_error(filename,linenum,"Non-constant width range on parameter decl.\n");intwidth=std::abs(children[1]->range_left-children[1]->range_right)+1;if(children[0]->type==AST_REALVALUE){RTLIL::Constconstvalue=children[0]->realAsConst(width);log_file_warning(filename,linenum,"converting real value %e to binary %s.\n",children[0]->realvalue,log_signal(constvalue));deletechildren[0];children[0]=mkconst_bits(constvalue.bits,sign_hint);did_something=true;}if(children[0]->type==AST_CONSTANT){if(width!=int(children[0]->bits.size())){RTLIL::SigSpecsig(children[0]->bits);sig.extend_u0(width,children[0]->is_signed);AstNode*old_child_0=children[0];children[0]=mkconst_bits(sig.as_const().bits,is_signed);deleteold_child_0;}children[0]->is_signed=is_signed;}range_valid=true;range_swapped=children[1]->range_swapped;range_left=children[1]->range_left;range_right=children[1]->range_right;}elseif(children.size()>1&&children[1]->type==AST_REALVALUE&&children[0]->type==AST_CONSTANT){doubleas_realvalue=children[0]->asReal(sign_hint);deletechildren[0];children[0]=newAstNode(AST_REALVALUE);children[0]->realvalue=as_realvalue;did_something=true;}}// annotate identifiers using scope resolution and create auto-wires as neededif(type==AST_IDENTIFIER){if(current_scope.count(str)==0){for(autonode:current_ast_mod->children){if((node->type==AST_PARAMETER||node->type==AST_LOCALPARAM||node->type==AST_WIRE||node->type==AST_AUTOWIRE||node->type==AST_GENVAR||node->type==AST_MEMORY||node->type==AST_FUNCTION||node->type==AST_TASK||node->type==AST_DPI_FUNCTION)&&str==node->str){current_scope[node->str]=node;break;}}}if(current_scope.count(str)==0){if(flag_autowire||str=="\\$global_clock"){AstNode*auto_wire=newAstNode(AST_AUTOWIRE);auto_wire->str=str;current_ast_mod->children.push_back(auto_wire);current_scope[str]=auto_wire;did_something=true;}else{log_file_error(filename,linenum,"Identifier `%s' is implicitly declared and `default_nettype is set to none.\n",str.c_str());}}if(id2ast!=current_scope[str]){id2ast=current_scope[str];did_something=true;}}// split memory access with bit select to individual statementsif(type==AST_IDENTIFIER&&children.size()==2&&children[0]->type==AST_RANGE&&children[1]->type==AST_RANGE&&!in_lvalue){if(id2ast==NULL||id2ast->type!=AST_MEMORY||children[0]->children.size()!=1)log_file_error(filename,linenum,"Invalid bit-select on memory access!\n");intmem_width,mem_size,addr_bits;id2ast->meminfo(mem_width,mem_size,addr_bits);intdata_range_left=id2ast->children[0]->range_left;intdata_range_right=id2ast->children[0]->range_right;if(id2ast->children[0]->range_swapped)std::swap(data_range_left,data_range_right);std::stringstreamsstr;sstr<<"$mem2bits$"<<str<<"$"<<filename<<":"<<linenum<<"$"<<(autoidx++);std::stringwire_id=sstr.str();AstNode*wire=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(data_range_left,true),mkconst_int(data_range_right,true)));wire->str=wire_id;if(current_block)wire->attributes["\\nosync"]=AstNode::mkconst_int(1,false);current_ast_mod->children.push_back(wire);while(wire->simplify(true,false,false,1,-1,false,false)){}AstNode*data=clone();deletedata->children[1];data->children.pop_back();AstNode*assign=newAstNode(AST_ASSIGN_EQ,newAstNode(AST_IDENTIFIER),data);assign->children[0]->str=wire_id;assign->children[0]->was_checked=true;if(current_block){size_tassign_idx=0;while(assign_idx<current_block->children.size()&¤t_block->children[assign_idx]!=current_block_child)assign_idx++;log_assert(assign_idx<current_block->children.size());current_block->children.insert(current_block->children.begin()+assign_idx,assign);wire->is_reg=true;}else{AstNode*proc=newAstNode(AST_ALWAYS,newAstNode(AST_BLOCK));proc->children[0]->children.push_back(assign);current_ast_mod->children.push_back(proc);}newNode=newAstNode(AST_IDENTIFIER,children[1]->clone());newNode->str=wire_id;newNode->id2ast=wire;gotoapply_newNode;}if(type==AST_WHILE)log_file_error(filename,linenum,"While loops are only allowed in constant functions!\n");if(type==AST_REPEAT){AstNode*count=children[0];AstNode*body=children[1];// eval count expressionwhile(count->simplify(true,false,false,stage,32,true,false)){}if(count->type!=AST_CONSTANT)log_file_error(filename,linenum,"Repeat loops outside must have constant repeat counts!\n");// convert to a block with the body repeated n timestype=AST_BLOCK;children.clear();for(inti=0;i<count->bitsAsConst().as_int();i++)children.insert(children.begin(),body->clone());deletecount;deletebody;did_something=true;}// unroll for loops and generate-for blocksif((type==AST_GENFOR||type==AST_FOR)&&children.size()!=0){AstNode*init_ast=children[0];AstNode*while_ast=children[1];AstNode*next_ast=children[2];AstNode*body_ast=children[3];while(body_ast->type==AST_GENBLOCK&&body_ast->str.empty()&&body_ast->children.size()==1&&body_ast->children.at(0)->type==AST_GENBLOCK)body_ast=body_ast->children.at(0);if(init_ast->type!=AST_ASSIGN_EQ)log_file_error(filename,linenum,"Unsupported 1st expression of generate for-loop!\n");if(next_ast->type!=AST_ASSIGN_EQ)log_file_error(filename,linenum,"Unsupported 3rd expression of generate for-loop!\n");if(type==AST_GENFOR){if(init_ast->children[0]->id2ast==NULL||init_ast->children[0]->id2ast->type!=AST_GENVAR)log_file_error(filename,linenum,"Left hand side of 1st expression of generate for-loop is not a gen var!\n");if(next_ast->children[0]->id2ast==NULL||next_ast->children[0]->id2ast->type!=AST_GENVAR)log_file_error(filename,linenum,"Left hand side of 3rd expression of generate for-loop is not a gen var!\n");}else{if(init_ast->children[0]->id2ast==NULL||init_ast->children[0]->id2ast->type!=AST_WIRE)log_file_error(filename,linenum,"Left hand side of 1st expression of generate for-loop is not a register!\n");if(next_ast->children[0]->id2ast==NULL||next_ast->children[0]->id2ast->type!=AST_WIRE)log_file_error(filename,linenum,"Left hand side of 3rd expression of generate for-loop is not a register!\n");}if(init_ast->children[0]->id2ast!=next_ast->children[0]->id2ast)log_file_error(filename,linenum,"Incompatible left-hand sides in 1st and 3rd expression of generate for-loop!\n");// eval 1st expressionAstNode*varbuf=init_ast->children[1]->clone();{intexpr_width_hint=-1;boolexpr_sign_hint=true;varbuf->detectSignWidth(expr_width_hint,expr_sign_hint);while(varbuf->simplify(true,false,false,stage,32,true,false)){}}if(varbuf->type!=AST_CONSTANT)log_file_error(filename,linenum,"Right hand side of 1st expression of generate for-loop is not constant!\n");varbuf=newAstNode(AST_LOCALPARAM,varbuf);varbuf->str=init_ast->children[0]->str;AstNode*backup_scope_varbuf=current_scope[varbuf->str];current_scope[varbuf->str]=varbuf;size_tcurrent_block_idx=0;if(type==AST_FOR){while(current_block_idx<current_block->children.size()&¤t_block->children[current_block_idx]!=current_block_child)current_block_idx++;}while(1){// eval 2nd expressionAstNode*buf=while_ast->clone();{intexpr_width_hint=-1;boolexpr_sign_hint=true;buf->detectSignWidth(expr_width_hint,expr_sign_hint);while(buf->simplify(true,false,false,stage,expr_width_hint,expr_sign_hint,false)){}}if(buf->type!=AST_CONSTANT)log_file_error(filename,linenum,"2nd expression of generate for-loop is not constant!\n");if(buf->integer==0){deletebuf;break;}deletebuf;// expand bodyintindex=varbuf->children[0]->integer;if(body_ast->type==AST_GENBLOCK)buf=body_ast->clone();elsebuf=newAstNode(AST_GENBLOCK,body_ast->clone());if(buf->str.empty()){std::stringstreamsstr;sstr<<"$genblock$"<<filename<<":"<<linenum<<"$"<<(autoidx++);buf->str=sstr.str();}std::map<std::string,std::string>name_map;std::stringstreamsstr;sstr<<buf->str<<"["<<index<<"].";buf->expand_genblock(varbuf->str,sstr.str(),name_map);if(type==AST_GENFOR){for(size_ti=0;i<buf->children.size();i++){buf->children[i]->simplify(false,false,false,stage,-1,false,false);current_ast_mod->children.push_back(buf->children[i]);}}else{for(size_ti=0;i<buf->children.size();i++)current_block->children.insert(current_block->children.begin()+current_block_idx++,buf->children[i]);}buf->children.clear();deletebuf;// eval 3rd expressionbuf=next_ast->children[1]->clone();{intexpr_width_hint=-1;boolexpr_sign_hint=true;buf->detectSignWidth(expr_width_hint,expr_sign_hint);while(buf->simplify(true,false,false,stage,expr_width_hint,expr_sign_hint,true)){}}if(buf->type!=AST_CONSTANT)log_file_error(filename,linenum,"Right hand side of 3rd expression of generate for-loop is not constant!\n");deletevarbuf->children[0];varbuf->children[0]=buf;}if(type==AST_FOR){AstNode*buf=next_ast->clone();deletebuf->children[1];buf->children[1]=varbuf->children[0]->clone();current_block->children.insert(current_block->children.begin()+current_block_idx++,buf);}current_scope[varbuf->str]=backup_scope_varbuf;deletevarbuf;delete_children();did_something=true;}// check for local objects in unnamed blockif(type==AST_BLOCK&&str.empty()){for(size_ti=0;i<children.size();i++)if(children[i]->type==AST_WIRE||children[i]->type==AST_MEMORY||children[i]->type==AST_PARAMETER||children[i]->type==AST_LOCALPARAM)log_file_error(children[i]->filename,children[i]->linenum,"Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");}// transform block with nameif(type==AST_BLOCK&&!str.empty()){std::map<std::string,std::string>name_map;expand_genblock(std::string(),str+".",name_map);std::vector<AstNode*>new_children;for(size_ti=0;i<children.size();i++)if(children[i]->type==AST_WIRE||children[i]->type==AST_MEMORY||children[i]->type==AST_PARAMETER||children[i]->type==AST_LOCALPARAM){children[i]->simplify(false,false,false,stage,-1,false,false);current_ast_mod->children.push_back(children[i]);current_scope[children[i]->str]=children[i];}elsenew_children.push_back(children[i]);children.swap(new_children);did_something=true;str.clear();}// simplify unconditional generate blockif(type==AST_GENBLOCK&&children.size()!=0){if(!str.empty()){std::map<std::string,std::string>name_map;expand_genblock(std::string(),str+".",name_map);}for(size_ti=0;i<children.size();i++){children[i]->simplify(false,false,false,stage,-1,false,false);current_ast_mod->children.push_back(children[i]);}children.clear();did_something=true;}// simplify generate-if blocksif(type==AST_GENIF&&children.size()!=0){AstNode*buf=children[0]->clone();while(buf->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(buf->type!=AST_CONSTANT){// for (auto f : log_files)// dumpAst(f, "verilog-ast> ");log_file_error(filename,linenum,"Condition for generate if is not constant!\n");}if(buf->asBool()!=0){deletebuf;buf=children[1]->clone();}else{deletebuf;buf=children.size()>2?children[2]->clone():NULL;}if(buf){if(buf->type!=AST_GENBLOCK)buf=newAstNode(AST_GENBLOCK,buf);if(!buf->str.empty()){std::map<std::string,std::string>name_map;buf->expand_genblock(std::string(),buf->str+".",name_map);}for(size_ti=0;i<buf->children.size();i++){buf->children[i]->simplify(false,false,false,stage,-1,false,false);current_ast_mod->children.push_back(buf->children[i]);}buf->children.clear();deletebuf;}delete_children();did_something=true;}// simplify generate-case blocksif(type==AST_GENCASE&&children.size()!=0){AstNode*buf=children[0]->clone();while(buf->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(buf->type!=AST_CONSTANT){// for (auto f : log_files)// dumpAst(f, "verilog-ast> ");log_file_error(filename,linenum,"Condition for generate case is not constant!\n");}boolref_signed=buf->is_signed;RTLIL::Constref_value=buf->bitsAsConst();deletebuf;AstNode*selected_case=NULL;for(size_ti=1;i<children.size();i++){log_assert(children.at(i)->type==AST_COND||children.at(i)->type==AST_CONDX||children.at(i)->type==AST_CONDZ);AstNode*this_genblock=NULL;for(autochild:children.at(i)->children){log_assert(this_genblock==NULL);if(child->type==AST_GENBLOCK)this_genblock=child;}for(autochild:children.at(i)->children){if(child->type==AST_DEFAULT){if(selected_case==NULL)selected_case=this_genblock;continue;}if(child->type==AST_GENBLOCK)continue;buf=child->clone();while(buf->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(buf->type!=AST_CONSTANT){// for (auto f : log_files)// dumpAst(f, "verilog-ast> ");log_file_error(filename,linenum,"Expression in generate case is not constant!\n");}boolis_selected=RTLIL::const_eq(ref_value,buf->bitsAsConst(),ref_signed&&buf->is_signed,ref_signed&&buf->is_signed,1).as_bool();deletebuf;if(is_selected){selected_case=this_genblock;i=children.size();break;}}}if(selected_case!=NULL){log_assert(selected_case->type==AST_GENBLOCK);buf=selected_case->clone();if(!buf->str.empty()){std::map<std::string,std::string>name_map;buf->expand_genblock(std::string(),buf->str+".",name_map);}for(size_ti=0;i<buf->children.size();i++){buf->children[i]->simplify(false,false,false,stage,-1,false,false);current_ast_mod->children.push_back(buf->children[i]);}buf->children.clear();deletebuf;}delete_children();did_something=true;}// unroll cell arraysif(type==AST_CELLARRAY){if(!children.at(0)->range_valid)log_file_error(filename,linenum,"Non-constant array range on cell array.\n");newNode=newAstNode(AST_GENBLOCK);intnum=max(children.at(0)->range_left,children.at(0)->range_right)-min(children.at(0)->range_left,children.at(0)->range_right)+1;for(inti=0;i<num;i++){intidx=children.at(0)->range_left>children.at(0)->range_right?children.at(0)->range_right+i:children.at(0)->range_right-i;AstNode*new_cell=children.at(1)->clone();newNode->children.push_back(new_cell);new_cell->str+=stringf("[%d]",idx);if(new_cell->type==AST_PRIMITIVE){log_file_error(filename,linenum,"Cell arrays of primitives are currently not supported.\n");}else{log_assert(new_cell->children.at(0)->type==AST_CELLTYPE);new_cell->children.at(0)->str=stringf("$array:%d:%d:%s",i,num,new_cell->children.at(0)->str.c_str());}}gotoapply_newNode;}// replace primitives with assignmentsif(type==AST_PRIMITIVE){if(children.size()<2)log_file_error(filename,linenum,"Insufficient number of arguments for primitive `%s'!\n",str.c_str());std::vector<AstNode*>children_list;for(autochild:children){log_assert(child->type==AST_ARGUMENT);log_assert(child->children.size()==1);children_list.push_back(child->children[0]);child->children.clear();deletechild;}children.clear();if(str=="bufif0"||str=="bufif1"||str=="notif0"||str=="notif1"){if(children_list.size()!=3)log_file_error(filename,linenum,"Invalid number of arguments for primitive `%s'!\n",str.c_str());std::vector<RTLIL::State>z_const(1,RTLIL::State::Sz);AstNode*mux_input=children_list.at(1);if(str=="notif0"||str=="notif1"){mux_input=newAstNode(AST_BIT_NOT,mux_input);}AstNode*node=newAstNode(AST_TERNARY,children_list.at(2));if(str=="bufif0"){node->children.push_back(AstNode::mkconst_bits(z_const,false));node->children.push_back(mux_input);}else{node->children.push_back(mux_input);node->children.push_back(AstNode::mkconst_bits(z_const,false));}str.clear();type=AST_ASSIGN;children.push_back(children_list.at(0));children.back()->was_checked=true;children.push_back(node);did_something=true;}else{AstNodeTypeop_type=AST_NONE;boolinvert_results=false;if(str=="and")op_type=AST_BIT_AND;if(str=="nand")op_type=AST_BIT_AND,invert_results=true;if(str=="or")op_type=AST_BIT_OR;if(str=="nor")op_type=AST_BIT_OR,invert_results=true;if(str=="xor")op_type=AST_BIT_XOR;if(str=="xnor")op_type=AST_BIT_XOR,invert_results=true;if(str=="buf")op_type=AST_POS;if(str=="not")op_type=AST_POS,invert_results=true;log_assert(op_type!=AST_NONE);AstNode*node=children_list[1];if(op_type!=AST_POS)for(size_ti=2;i<children_list.size();i++)node=newAstNode(op_type,node,children_list[i]);if(invert_results)node=newAstNode(AST_BIT_NOT,node);str.clear();type=AST_ASSIGN;children.push_back(children_list[0]);children.back()->was_checked=true;children.push_back(node);did_something=true;}}// replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with// a big case block that selects the correct single-bit assignment.if(type==AST_ASSIGN_EQ||type==AST_ASSIGN_LE){if(children[0]->type!=AST_IDENTIFIER||children[0]->children.size()==0)gotoskip_dynamic_range_lvalue_expansion;if(children[0]->children[0]->range_valid||did_something)gotoskip_dynamic_range_lvalue_expansion;if(children[0]->id2ast==NULL||children[0]->id2ast->type!=AST_WIRE)gotoskip_dynamic_range_lvalue_expansion;if(!children[0]->id2ast->range_valid)gotoskip_dynamic_range_lvalue_expansion;intsource_width=children[0]->id2ast->range_left-children[0]->id2ast->range_right+1;intresult_width=1;AstNode*shift_expr=NULL;AstNode*range=children[0]->children[0];if(range->children.size()==1){shift_expr=range->children[0]->clone();}else{shift_expr=range->children[1]->clone();AstNode*left_at_zero_ast=range->children[0]->clone();AstNode*right_at_zero_ast=range->children[1]->clone();while(left_at_zero_ast->simplify(true,true,false,stage,-1,false,false)){}while(right_at_zero_ast->simplify(true,true,false,stage,-1,false,false)){}if(left_at_zero_ast->type!=AST_CONSTANT||right_at_zero_ast->type!=AST_CONSTANT)log_file_error(filename,linenum,"Unsupported expression on dynamic range select on signal `%s'!\n",str.c_str());result_width=abs(int(left_at_zero_ast->integer-right_at_zero_ast->integer))+1;}did_something=true;newNode=newAstNode(AST_CASE,shift_expr);for(inti=0;i<=source_width-result_width;i++){intstart_bit=children[0]->id2ast->range_right+i;AstNode*cond=newAstNode(AST_COND,mkconst_int(start_bit,true));AstNode*lvalue=children[0]->clone();lvalue->delete_children();lvalue->children.push_back(newAstNode(AST_RANGE,mkconst_int(start_bit+result_width-1,true),mkconst_int(start_bit,true)));cond->children.push_back(newAstNode(AST_BLOCK,newAstNode(type,lvalue,children[1]->clone())));newNode->children.push_back(cond);}gotoapply_newNode;}skip_dynamic_range_lvalue_expansion:;if(stage>1&&(type==AST_ASSERT||type==AST_ASSUME||type==AST_LIVE||type==AST_FAIR||type==AST_COVER)&¤t_block!=NULL){std::stringstreamsstr;sstr<<"$formal$"<<filename<<":"<<linenum<<"$"<<(autoidx++);std::stringid_check=sstr.str()+"_CHECK",id_en=sstr.str()+"_EN";AstNode*wire_check=newAstNode(AST_WIRE);wire_check->str=id_check;wire_check->was_checked=true;current_ast_mod->children.push_back(wire_check);current_scope[wire_check->str]=wire_check;while(wire_check->simplify(true,false,false,1,-1,false,false)){}AstNode*wire_en=newAstNode(AST_WIRE);wire_en->str=id_en;wire_en->was_checked=true;current_ast_mod->children.push_back(wire_en);if(current_always_clocked){current_ast_mod->children.push_back(newAstNode(AST_INITIAL,newAstNode(AST_BLOCK,newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),AstNode::mkconst_int(0,false,1)))));current_ast_mod->children.back()->children[0]->children[0]->children[0]->str=id_en;current_ast_mod->children.back()->children[0]->children[0]->children[0]->was_checked=true;}current_scope[wire_en->str]=wire_en;while(wire_en->simplify(true,false,false,1,-1,false,false)){}std::vector<RTLIL::State>x_bit;x_bit.push_back(RTLIL::State::Sx);AstNode*assign_check=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_bits(x_bit,false));assign_check->children[0]->str=id_check;assign_check->children[0]->was_checked=true;AstNode*assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_int(0,false,1));assign_en->children[0]->str=id_en;assign_en->children[0]->was_checked=true;AstNode*default_signals=newAstNode(AST_BLOCK);default_signals->children.push_back(assign_check);default_signals->children.push_back(assign_en);current_top_block->children.insert(current_top_block->children.begin(),default_signals);assign_check=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),newAstNode(AST_REDUCE_BOOL,children[0]->clone()));assign_check->children[0]->str=id_check;assign_check->children[0]->was_checked=true;if(current_always==nullptr||current_always->type!=AST_INITIAL){assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_int(1,false,1));}else{assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),newAstNode(AST_FCALL));assign_en->children[1]->str="\\$initstate";}assign_en->children[0]->str=id_en;assign_en->children[0]->was_checked=true;newNode=newAstNode(AST_BLOCK);newNode->children.push_back(assign_check);newNode->children.push_back(assign_en);AstNode*assertnode=newAstNode(type);assertnode->str=str;assertnode->children.push_back(newAstNode(AST_IDENTIFIER));assertnode->children.push_back(newAstNode(AST_IDENTIFIER));assertnode->children[0]->str=id_check;assertnode->children[1]->str=id_en;assertnode->attributes.swap(attributes);current_ast_mod->children.push_back(assertnode);gotoapply_newNode;}if(stage>1&&(type==AST_ASSERT||type==AST_ASSUME||type==AST_LIVE||type==AST_FAIR||type==AST_COVER)&&children.size()==1){children.push_back(mkconst_int(1,false,1));did_something=true;}// found right-hand side identifier for memory -> replace with memory read portif(stage>1&&type==AST_IDENTIFIER&&id2ast!=NULL&&id2ast->type==AST_MEMORY&&!in_lvalue&&children.size()==1&&children[0]->type==AST_RANGE&&children[0]->children.size()==1){newNode=newAstNode(AST_MEMRD,children[0]->children[0]->clone());newNode->str=str;newNode->id2ast=id2ast;gotoapply_newNode;}// assignment with nontrivial member in left-hand concat expression -> split assignmentif((type==AST_ASSIGN_EQ||type==AST_ASSIGN_LE)&&children[0]->type==AST_CONCAT&&width_hint>0){boolfound_nontrivial_member=false;for(autochild:children[0]->children){if(child->type==AST_IDENTIFIER&&child->id2ast!=NULL&&child->id2ast->type==AST_MEMORY)found_nontrivial_member=true;}if(found_nontrivial_member){newNode=newAstNode(AST_BLOCK);AstNode*wire_tmp=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(width_hint-1,true),mkconst_int(0,true)));wire_tmp->str=stringf("$splitcmplxassign$%s:%d$%d",filename.c_str(),linenum,autoidx++);current_ast_mod->children.push_back(wire_tmp);current_scope[wire_tmp->str]=wire_tmp;wire_tmp->attributes["\\nosync"]=AstNode::mkconst_int(1,false);while(wire_tmp->simplify(true,false,false,1,-1,false,false)){}wire_tmp->is_logic=true;AstNode*wire_tmp_id=newAstNode(AST_IDENTIFIER);wire_tmp_id->str=wire_tmp->str;newNode->children.push_back(newAstNode(AST_ASSIGN_EQ,wire_tmp_id,children[1]->clone()));newNode->children.back()->was_checked=true;intcursor=0;for(autochild:children[0]->children){intchild_width_hint=-1;boolchild_sign_hint=true;child->detectSignWidth(child_width_hint,child_sign_hint);AstNode*rhs=wire_tmp_id->clone();rhs->children.push_back(newAstNode(AST_RANGE,AstNode::mkconst_int(cursor+child_width_hint-1,true),AstNode::mkconst_int(cursor,true)));newNode->children.push_back(newAstNode(type,child->clone(),rhs));cursor+=child_width_hint;}gotoapply_newNode;}}// assignment with memory in left-hand side expression -> replace with memory write portif(stage>1&&(type==AST_ASSIGN_EQ||type==AST_ASSIGN_LE)&&children[0]->type==AST_IDENTIFIER&&children[0]->id2ast&&children[0]->id2ast->type==AST_MEMORY&&children[0]->id2ast->children.size()>=2&&children[0]->id2ast->children[0]->range_valid&&children[0]->id2ast->children[1]->range_valid&&(children[0]->children.size()==1||children[0]->children.size()==2)&&children[0]->children[0]->type==AST_RANGE){std::stringstreamsstr;sstr<<"$memwr$"<<children[0]->str<<"$"<<filename<<":"<<linenum<<"$"<<(autoidx++);std::stringid_addr=sstr.str()+"_ADDR",id_data=sstr.str()+"_DATA",id_en=sstr.str()+"_EN";intmem_width,mem_size,addr_bits;boolmem_signed=children[0]->id2ast->is_signed;children[0]->id2ast->meminfo(mem_width,mem_size,addr_bits);intdata_range_left=children[0]->id2ast->children[0]->range_left;intdata_range_right=children[0]->id2ast->children[0]->range_right;intmem_data_range_offset=std::min(data_range_left,data_range_right);intaddr_width_hint=-1;booladdr_sign_hint=true;children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint,addr_sign_hint);addr_bits=std::max(addr_bits,addr_width_hint);AstNode*wire_addr=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(addr_bits-1,true),mkconst_int(0,true)));wire_addr->str=id_addr;wire_addr->was_checked=true;current_ast_mod->children.push_back(wire_addr);current_scope[wire_addr->str]=wire_addr;while(wire_addr->simplify(true,false,false,1,-1,false,false)){}AstNode*wire_data=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(mem_width-1,true),mkconst_int(0,true)));wire_data->str=id_data;wire_data->was_checked=true;wire_data->is_signed=mem_signed;current_ast_mod->children.push_back(wire_data);current_scope[wire_data->str]=wire_data;while(wire_data->simplify(true,false,false,1,-1,false,false)){}AstNode*wire_en=nullptr;if(current_always->type!=AST_INITIAL){wire_en=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(mem_width-1,true),mkconst_int(0,true)));wire_en->str=id_en;wire_en->was_checked=true;current_ast_mod->children.push_back(wire_en);current_scope[wire_en->str]=wire_en;while(wire_en->simplify(true,false,false,1,-1,false,false)){}}std::vector<RTLIL::State>x_bits_addr,x_bits_data,set_bits_en;for(inti=0;i<addr_bits;i++)x_bits_addr.push_back(RTLIL::State::Sx);for(inti=0;i<mem_width;i++)x_bits_data.push_back(RTLIL::State::Sx);for(inti=0;i<mem_width;i++)set_bits_en.push_back(RTLIL::State::S1);AstNode*assign_addr=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_bits(x_bits_addr,false));assign_addr->children[0]->str=id_addr;assign_addr->children[0]->was_checked=true;AstNode*assign_data=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_bits(x_bits_data,false));assign_data->children[0]->str=id_data;assign_data->children[0]->was_checked=true;AstNode*assign_en=nullptr;if(current_always->type!=AST_INITIAL){assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_int(0,false,mem_width));assign_en->children[0]->str=id_en;assign_en->children[0]->was_checked=true;}AstNode*default_signals=newAstNode(AST_BLOCK);default_signals->children.push_back(assign_addr);default_signals->children.push_back(assign_data);if(current_always->type!=AST_INITIAL)default_signals->children.push_back(assign_en);current_top_block->children.insert(current_top_block->children.begin(),default_signals);assign_addr=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),children[0]->children[0]->children[0]->clone());assign_addr->children[0]->str=id_addr;assign_addr->children[0]->was_checked=true;if(children[0]->children.size()==2){if(children[0]->children[1]->range_valid){intoffset=children[0]->children[1]->range_right;intwidth=children[0]->children[1]->range_left-offset+1;offset-=mem_data_range_offset;std::vector<RTLIL::State>padding_x(offset,RTLIL::State::Sx);assign_data=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),newAstNode(AST_CONCAT,mkconst_bits(padding_x,false),children[1]->clone()));assign_data->children[0]->str=id_data;assign_data->children[0]->was_checked=true;if(current_always->type!=AST_INITIAL){for(inti=0;i<mem_width;i++)set_bits_en[i]=offset<=i&&i<offset+width?RTLIL::State::S1:RTLIL::State::S0;assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_bits(set_bits_en,false));assign_en->children[0]->str=id_en;assign_en->children[0]->was_checked=true;}}else{AstNode*the_range=children[0]->children[1];AstNode*left_at_zero_ast=the_range->children[0]->clone();AstNode*right_at_zero_ast=the_range->children.size()>=2?the_range->children[1]->clone():left_at_zero_ast->clone();AstNode*offset_ast=right_at_zero_ast->clone();if(mem_data_range_offset)offset_ast=newAstNode(AST_SUB,offset_ast,mkconst_int(mem_data_range_offset,true));while(left_at_zero_ast->simplify(true,true,false,1,-1,false,false)){}while(right_at_zero_ast->simplify(true,true,false,1,-1,false,false)){}if(left_at_zero_ast->type!=AST_CONSTANT||right_at_zero_ast->type!=AST_CONSTANT)log_file_error(filename,linenum,"Unsupported expression on dynamic range select on signal `%s'!\n",str.c_str());intwidth=abs(int(left_at_zero_ast->integer-right_at_zero_ast->integer))+1;assign_data=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),newAstNode(AST_SHIFT_LEFT,children[1]->clone(),offset_ast->clone()));assign_data->children[0]->str=id_data;assign_data->children[0]->was_checked=true;if(current_always->type!=AST_INITIAL){for(inti=0;i<mem_width;i++)set_bits_en[i]=i<width?RTLIL::State::S1:RTLIL::State::S0;assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),newAstNode(AST_SHIFT_LEFT,mkconst_bits(set_bits_en,false),offset_ast->clone()));assign_en->children[0]->str=id_en;assign_en->children[0]->was_checked=true;}deleteleft_at_zero_ast;deleteright_at_zero_ast;deleteoffset_ast;}}else{assign_data=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),children[1]->clone());assign_data->children[0]->str=id_data;assign_data->children[0]->was_checked=true;if(current_always->type!=AST_INITIAL){assign_en=newAstNode(AST_ASSIGN_LE,newAstNode(AST_IDENTIFIER),mkconst_bits(set_bits_en,false));assign_en->children[0]->str=id_en;assign_en->children[0]->was_checked=true;}}newNode=newAstNode(AST_BLOCK);newNode->children.push_back(assign_addr);newNode->children.push_back(assign_data);if(current_always->type!=AST_INITIAL)newNode->children.push_back(assign_en);AstNode*wrnode=newAstNode(current_always->type==AST_INITIAL?AST_MEMINIT:AST_MEMWR);wrnode->children.push_back(newAstNode(AST_IDENTIFIER));wrnode->children.push_back(newAstNode(AST_IDENTIFIER));if(current_always->type!=AST_INITIAL)wrnode->children.push_back(newAstNode(AST_IDENTIFIER));elsewrnode->children.push_back(AstNode::mkconst_int(1,false));wrnode->str=children[0]->str;wrnode->id2ast=children[0]->id2ast;wrnode->children[0]->str=id_addr;wrnode->children[1]->str=id_data;if(current_always->type!=AST_INITIAL)wrnode->children[2]->str=id_en;current_ast_mod->children.push_back(wrnode);gotoapply_newNode;}// replace function and task calls with the code from the function or taskif((type==AST_FCALL||type==AST_TCALL)&&!str.empty()){if(type==AST_FCALL){if(str=="\\$initstate"){intmyidx=autoidx++;AstNode*wire=newAstNode(AST_WIRE);wire->str=stringf("$initstate$%d_wire",myidx);current_ast_mod->children.push_back(wire);while(wire->simplify(true,false,false,1,-1,false,false)){}AstNode*cell=newAstNode(AST_CELL,newAstNode(AST_CELLTYPE),newAstNode(AST_ARGUMENT,newAstNode(AST_IDENTIFIER)));cell->str=stringf("$initstate$%d",myidx);cell->children[0]->str="$initstate";cell->children[1]->str="\\Y";cell->children[1]->children[0]->str=wire->str;cell->children[1]->children[0]->id2ast=wire;current_ast_mod->children.push_back(cell);while(cell->simplify(true,false,false,1,-1,false,false)){}newNode=newAstNode(AST_IDENTIFIER);newNode->str=wire->str;newNode->id2ast=wire;gotoapply_newNode;}if(str=="\\$past"){if(width_hint<0)gotoreplace_fcall_later;intnum_steps=1;if(GetSize(children)!=1&&GetSize(children)!=2)log_file_error(filename,linenum,"System function %s got %d arguments, expected 1 or 2.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));if(!current_always_clocked)log_file_error(filename,linenum,"System function %s is only allowed in clocked blocks.\n",RTLIL::unescape_id(str).c_str());if(GetSize(children)==2){AstNode*buf=children[1]->clone();while(buf->simplify(true,false,false,stage,-1,false,false)){}if(buf->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant value.\n",str.c_str());num_steps=buf->asInt(true);deletebuf;}AstNode*block=nullptr;for(autochild:current_always->children)if(child->type==AST_BLOCK)block=child;log_assert(block!=nullptr);if(num_steps==0){newNode=children[0]->clone();gotoapply_newNode;}intmyidx=autoidx++;AstNode*outreg=nullptr;for(inti=0;i<num_steps;i++){AstNode*reg=newAstNode(AST_WIRE,newAstNode(AST_RANGE,mkconst_int(width_hint-1,true),mkconst_int(0,true)));reg->str=stringf("$past$%s:%d$%d$%d",filename.c_str(),linenum,myidx,i);reg->is_reg=true;current_ast_mod->children.push_back(reg);while(reg->simplify(true,false,false,1,-1,false,false)){}AstNode*regid=newAstNode(AST_IDENTIFIER);regid->str=reg->str;regid->id2ast=reg;regid->was_checked=true;AstNode*rhs=nullptr;if(outreg==nullptr){rhs=children.at(0)->clone();}else{rhs=newAstNode(AST_IDENTIFIER);rhs->str=outreg->str;rhs->id2ast=outreg;}block->children.push_back(newAstNode(AST_ASSIGN_LE,regid,rhs));outreg=reg;}newNode=newAstNode(AST_IDENTIFIER);newNode->str=outreg->str;newNode->id2ast=outreg;gotoapply_newNode;}if(str=="\\$stable"||str=="\\$rose"||str=="\\$fell"||str=="\\$changed"){if(GetSize(children)!=1)log_file_error(filename,linenum,"System function %s got %d arguments, expected 1.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));if(!current_always_clocked)log_file_error(filename,linenum,"System function %s is only allowed in clocked blocks.\n",RTLIL::unescape_id(str).c_str());AstNode*present=children.at(0)->clone();AstNode*past=clone();past->str="\\$past";if(str=="\\$stable")newNode=newAstNode(AST_EQ,past,present);elseif(str=="\\$changed")newNode=newAstNode(AST_NE,past,present);elseif(str=="\\$rose")newNode=newAstNode(AST_LOGIC_AND,newAstNode(AST_LOGIC_NOT,newAstNode(AST_BIT_AND,past,mkconst_int(1,false))),newAstNode(AST_BIT_AND,present,mkconst_int(1,false)));elseif(str=="\\$fell")newNode=newAstNode(AST_LOGIC_AND,newAstNode(AST_BIT_AND,past,mkconst_int(1,false)),newAstNode(AST_LOGIC_NOT,newAstNode(AST_BIT_AND,present,mkconst_int(1,false))));elselog_abort();gotoapply_newNode;}// $anyconst and $anyseq are mapped in AstNode::genRTLIL()if(str=="\\$anyconst"||str=="\\$anyseq"||str=="\\$allconst"||str=="\\$allseq"){recursion_counter--;returnfalse;}if(str=="\\$clog2"){if(children.size()!=1)log_file_error(filename,linenum,"System function %s got %d arguments, expected 1.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));AstNode*buf=children[0]->clone();while(buf->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(buf->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant value.\n",str.c_str());RTLIL::Constarg_value=buf->bitsAsConst();if(arg_value.as_bool())arg_value=const_sub(arg_value,1,false,false,GetSize(arg_value));deletebuf;uint32_tresult=0;for(size_ti=0;i<arg_value.bits.size();i++)if(arg_value.bits.at(i)==RTLIL::State::S1)result=i+1;newNode=mkconst_int(result,true);gotoapply_newNode;}if(str=="\\$size"||str=="\\$bits"){if(str=="\\$bits"&&children.size()!=1)log_file_error(filename,linenum,"System function %s got %d arguments, expected 1.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));if(str=="\\$size"&&children.size()!=1&&children.size()!=2)log_file_error(filename,linenum,"System function %s got %d arguments, expected 1 or 2.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));intdim=1;if(str=="\\$size"&&children.size()==2){AstNode*buf=children[1]->clone();// Evaluate constant expressionwhile(buf->simplify(true,false,false,stage,width_hint,sign_hint,false)){}dim=buf->asInt(false);deletebuf;}AstNode*buf=children[0]->clone();intmem_depth=1;AstNode*id_ast=NULL;// Is this needed?//while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }buf->detectSignWidth(width_hint,sign_hint);if(buf->type==AST_IDENTIFIER){id_ast=buf->id2ast;if(id_ast==NULL&¤t_scope.count(buf->str))id_ast=current_scope.at(buf->str);if(!id_ast)log_file_error(filename,linenum,"Failed to resolve identifier %s for width detection!\n",buf->str.c_str());if(id_ast->type==AST_MEMORY){// We got here only if the argument is a memory// Otherwise $size() and $bits() return the expression widthAstNode*mem_range=id_ast->children[1];if(str=="\\$bits"){if(mem_range->type==AST_RANGE){if(!mem_range->range_valid)log_file_error(filename,linenum,"Failed to detect width of memory access `%s'!\n",buf->str.c_str());mem_depth=mem_range->range_left-mem_range->range_right+1;}elselog_file_error(filename,linenum,"Unknown memory depth AST type in `%s'!\n",buf->str.c_str());}else{// $size()if(mem_range->type==AST_RANGE){if(!mem_range->range_valid)log_file_error(filename,linenum,"Failed to detect width of memory access `%s'!\n",buf->str.c_str());intdims;if(id_ast->multirange_dimensions.empty())dims=1;elsedims=GetSize(id_ast->multirange_dimensions)/2;if(dim==1)width_hint=(dims>1)?id_ast->multirange_dimensions[1]:(mem_range->range_left-mem_range->range_right+1);elseif(dim<=dims){width_hint=id_ast->multirange_dimensions[2*dim-1];}elseif((dim>dims+1)||(dim<0))log_file_error(filename,linenum,"Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n",dim,buf->str.c_str(),dims+1);}elselog_file_error(filename,linenum,"Unknown memory depth AST type in `%s'!\n",buf->str.c_str());}}}deletebuf;newNode=mkconst_int(width_hint*mem_depth,false);gotoapply_newNode;}if(str=="\\$ln"||str=="\\$log10"||str=="\\$exp"||str=="\\$sqrt"||str=="\\$pow"||str=="\\$floor"||str=="\\$ceil"||str=="\\$sin"||str=="\\$cos"||str=="\\$tan"||str=="\\$asin"||str=="\\$acos"||str=="\\$atan"||str=="\\$atan2"||str=="\\$hypot"||str=="\\$sinh"||str=="\\$cosh"||str=="\\$tanh"||str=="\\$asinh"||str=="\\$acosh"||str=="\\$atanh"||str=="\\$rtoi"||str=="\\$itor"){boolfunc_with_two_arguments=str=="\\$pow"||str=="\\$atan2"||str=="\\$hypot";doublex=0,y=0;if(func_with_two_arguments){if(children.size()!=2)log_file_error(filename,linenum,"System function %s got %d arguments, expected 2.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));}else{if(children.size()!=1)log_file_error(filename,linenum,"System function %s got %d arguments, expected 1.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));}if(children.size()>=1){while(children[0]->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(!children[0]->isConst())log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant argument.\n",RTLIL::unescape_id(str).c_str());intchild_width_hint=width_hint;boolchild_sign_hint=sign_hint;children[0]->detectSignWidth(child_width_hint,child_sign_hint);x=children[0]->asReal(child_sign_hint);}if(children.size()>=2){while(children[1]->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(!children[1]->isConst())log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant argument.\n",RTLIL::unescape_id(str).c_str());intchild_width_hint=width_hint;boolchild_sign_hint=sign_hint;children[1]->detectSignWidth(child_width_hint,child_sign_hint);y=children[1]->asReal(child_sign_hint);}if(str=="\\$rtoi"){newNode=AstNode::mkconst_int(x,true);}else{newNode=newAstNode(AST_REALVALUE);if(str=="\\$ln")newNode->realvalue=::log(x);elseif(str=="\\$log10")newNode->realvalue=::log10(x);elseif(str=="\\$exp")newNode->realvalue=::exp(x);elseif(str=="\\$sqrt")newNode->realvalue=::sqrt(x);elseif(str=="\\$pow")newNode->realvalue=::pow(x,y);elseif(str=="\\$floor")newNode->realvalue=::floor(x);elseif(str=="\\$ceil")newNode->realvalue=::ceil(x);elseif(str=="\\$sin")newNode->realvalue=::sin(x);elseif(str=="\\$cos")newNode->realvalue=::cos(x);elseif(str=="\\$tan")newNode->realvalue=::tan(x);elseif(str=="\\$asin")newNode->realvalue=::asin(x);elseif(str=="\\$acos")newNode->realvalue=::acos(x);elseif(str=="\\$atan")newNode->realvalue=::atan(x);elseif(str=="\\$atan2")newNode->realvalue=::atan2(x,y);elseif(str=="\\$hypot")newNode->realvalue=::hypot(x,y);elseif(str=="\\$sinh")newNode->realvalue=::sinh(x);elseif(str=="\\$cosh")newNode->realvalue=::cosh(x);elseif(str=="\\$tanh")newNode->realvalue=::tanh(x);elseif(str=="\\$asinh")newNode->realvalue=::asinh(x);elseif(str=="\\$acosh")newNode->realvalue=::acosh(x);elseif(str=="\\$atanh")newNode->realvalue=::atanh(x);elseif(str=="\\$itor")newNode->realvalue=x;elselog_abort();}gotoapply_newNode;}if(current_scope.count(str)!=0&¤t_scope[str]->type==AST_DPI_FUNCTION){AstNode*dpi_decl=current_scope[str];std::stringrtype,fname;std::vector<std::string>argtypes;std::vector<AstNode*>args;rtype=RTLIL::unescape_id(dpi_decl->children.at(0)->str);fname=RTLIL::unescape_id(dpi_decl->children.at(1)->str);for(inti=2;i<GetSize(dpi_decl->children);i++){if(i-2>=GetSize(children))log_file_error(filename,linenum,"Insufficient number of arguments in DPI function call.\n");argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str));args.push_back(children.at(i-2)->clone());while(args.back()->simplify(true,false,false,stage,-1,false,true)){}if(args.back()->type!=AST_CONSTANT&&args.back()->type!=AST_REALVALUE)log_file_error(filename,linenum,"Failed to evaluate DPI function with non-constant argument.\n");}newNode=dpi_call(rtype,fname,argtypes,args);for(autoarg:args)deletearg;gotoapply_newNode;}if(current_scope.count(str)==0||current_scope[str]->type!=AST_FUNCTION)log_file_error(filename,linenum,"Can't resolve function name `%s'.\n",str.c_str());}if(type==AST_TCALL){if(str=="$finish"||str=="$stop"){if(!current_always||current_always->type!=AST_INITIAL)log_file_error(filename,linenum,"System task `%s' outside initial block is unsupported.\n",str.c_str());log_file_error(filename,linenum,"System task `%s' executed.\n",str.c_str());}if(str=="\\$readmemh"||str=="\\$readmemb"){if(GetSize(children)<2||GetSize(children)>4)log_file_error(filename,linenum,"System function %s got %d arguments, expected 2-4.\n",RTLIL::unescape_id(str).c_str(),int(children.size()));AstNode*node_filename=children[0]->clone();while(node_filename->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(node_filename->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant 1st argument.\n",str.c_str());AstNode*node_memory=children[1]->clone();while(node_memory->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(node_memory->type!=AST_IDENTIFIER||node_memory->id2ast==nullptr||node_memory->id2ast->type!=AST_MEMORY)log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-memory 2nd argument.\n",str.c_str());intstart_addr=-1,finish_addr=-1;if(GetSize(children)>2){AstNode*node_addr=children[2]->clone();while(node_addr->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(node_addr->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant 3rd argument.\n",str.c_str());start_addr=int(node_addr->asInt(false));}if(GetSize(children)>3){AstNode*node_addr=children[3]->clone();while(node_addr->simplify(true,false,false,stage,width_hint,sign_hint,false)){}if(node_addr->type!=AST_CONSTANT)log_file_error(filename,linenum,"Failed to evaluate system function `%s' with non-constant 4th argument.\n",str.c_str());finish_addr=int(node_addr->asInt(false));}boolunconditional_init=false;if(current_always->type==AST_INITIAL){pool<AstNode*>queue;log_assert(current_always->children[0]->type==AST_BLOCK);queue.insert(current_always->children[0]);while(!unconditional_init&&!queue.empty()){pool<AstNode*>next_queue;for(auton:queue)for(autoc:n->children){if(c==this)unconditional_init=true;next_queue.insert(c);}next_queue.swap(queue);}}newNode=readmem(str=="\\$readmemh",node_filename->bitsAsConst().decode_string(),node_memory->id2ast,start_addr,finish_addr,unconditional_init);deletenode_filename;deletenode_memory;gotoapply_newNode;}if(current_scope.count(str)==0||current_scope[str]->type!=AST_TASK)log_file_error(filename,linenum,"Can't resolve task name `%s'.\n",str.c_str());}AstNode*decl=current_scope[str];std::stringstreamsstr;sstr<<"$func$"<<str<<"$"<<filename<<":"<<linenum<<"$"<<(autoidx++)<<"$";std::stringprefix=sstr.str();boolrecommend_const_eval=false;boolrequire_const_eval=in_param?false:has_const_only_constructs(recommend_const_eval);if((in_param||recommend_const_eval||require_const_eval)&&!decl->attributes.count("\\via_celltype")){boolall_args_const=true;for(autochild:children){while(child->simplify(true,false,false,1,-1,false,true)){}if(child->type!=AST_CONSTANT)all_args_const=false;}if(all_args_const){AstNode*func_workspace=current_scope[str]->clone();newNode=func_workspace->eval_const_function(this);deletefunc_workspace;gotoapply_newNode;}if(in_param)log_file_error(filename,linenum,"Non-constant function call in constant expression.\n");if(require_const_eval)log_file_error(filename,linenum,"Function %s can only be called with constant arguments.\n",str.c_str());}size_targ_count=0;std::map<std::string,std::string>replace_rules;vector<AstNode*>added_mod_children;dict<std::string,AstNode*>wire_cache;vector<AstNode*>new_stmts;vector<AstNode*>output_assignments;if(current_block==NULL){log_assert(type==AST_FCALL);AstNode*wire=NULL;for(autochild:decl->children)if(child->type==AST_WIRE&&child->str==str)wire=child->clone();log_assert(wire!=NULL);wire->str=prefix+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*lvalue=newAstNode(AST_IDENTIFIER);lvalue->str=wire->str;AstNode*always=newAstNode(AST_ALWAYS,newAstNode(AST_BLOCK,newAstNode(AST_ASSIGN_EQ,lvalue,clone())));always->children[0]->children[0]->was_checked=true;current_ast_mod->children.push_back(always);gotoreplace_fcall_with_id;}if(decl->attributes.count("\\via_celltype")){std::stringcelltype=decl->attributes.at("\\via_celltype")->asAttrConst().decode_string();std::stringoutport=str;if(celltype.find(' ')!=std::string::npos){intpos=celltype.find(' ');outport=RTLIL::escape_id(celltype.substr(pos+1));celltype=RTLIL::escape_id(celltype.substr(0,pos));}elsecelltype=RTLIL::escape_id(celltype);AstNode*cell=newAstNode(AST_CELL,newAstNode(AST_CELLTYPE));cell->str=prefix.substr(0,GetSize(prefix)-1);cell->children[0]->str=celltype;for(autoattr:decl->attributes)if(attr.first.str().rfind("\\via_celltype_defparam_",0)==0){AstNode*cell_arg=newAstNode(AST_PARASET,attr.second->clone());cell_arg->str=RTLIL::escape_id(attr.first.substr(strlen("\\via_celltype_defparam_")));cell->children.push_back(cell_arg);}for(autochild: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=newAstNode(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?newAstNode(AST_ASSIGN_EQ,wire_id->clone(),arg):newAstNode(AST_ASSIGN_EQ,arg,wire_id->clone());assign->children[0]->was_checked=true;for(autoit=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=newAstNode(AST_ARGUMENT,wire_id);cell_arg->str=child->str==str?outport:child->str;cell->children.push_back(cell_arg);}current_ast_mod->children.push_back(cell);gotoreplace_fcall_with_id;}for(autochild:decl->children)if(child->type==AST_WIRE||child->type==AST_MEMORY||child->type==AST_PARAMETER||child->type==AST_LOCALPARAM){AstNode*wire=nullptr;if(wire_cache.count(child->str)){wire=wire_cache.at(child->str);if(wire->children.empty()){for(autoc:child->children)wire->children.push_back(c->clone());}elseif(!child->children.empty()){while(child->simplify(true,false,false,stage,-1,false,false)){}if(GetSize(child->children)==GetSize(wire->children)){for(inti=0;i<GetSize(child->children);i++)if(*child->children.at(i)!=*wire->children.at(i))gototcall_incompatible_wires;}else{tcall_incompatible_wires:log_file_error(filename,linenum,"Incompatible re-declaration of wire %s.\n",child->str.c_str());}}}else{wire=child->clone();wire->str=prefix+wire->str;wire->port_id=0;wire->is_input=false;wire->is_output=false;wire->is_reg=true;wire->attributes["\\nosync"]=AstNode::mkconst_int(1,false);wire_cache[child->str]=wire;current_ast_mod->children.push_back(wire);added_mod_children.push_back(wire);}if(child->type==AST_WIRE)while(wire->simplify(true,false,false,1,-1,false,false)){}replace_rules[child->str]=wire->str;current_scope[wire->str]=wire;if((child->is_input||child->is_output)&&arg_count<children.size()){AstNode*arg=children[arg_count++]->clone();AstNode*wire_id=newAstNode(AST_IDENTIFIER);wire_id->str=wire->str;AstNode*assign=child->is_input?newAstNode(AST_ASSIGN_EQ,wire_id,arg):newAstNode(AST_ASSIGN_EQ,arg,wire_id);assign->children[0]->was_checked=true;if(child->is_input)new_stmts.push_back(assign);elseoutput_assignments.push_back(assign);}}for(autochild:added_mod_children){child->replace_ids(prefix,replace_rules);while(child->simplify(true,false,false,1,-1,false,false)){}}for(autochild:decl->children)if(child->type!=AST_WIRE&&child->type!=AST_MEMORY&&child->type!=AST_PARAMETER&&child->type!=AST_LOCALPARAM){AstNode*stmt=child->clone();stmt->replace_ids(prefix,replace_rules);new_stmts.push_back(stmt);}new_stmts.insert(new_stmts.end(),output_assignments.begin(),output_assignments.end());for(autoit=current_block->children.begin();;it++){log_assert(it!=current_block->children.end());