diff options
Diffstat (limited to 'frontends/ast')
| -rw-r--r-- | frontends/ast/ast.cc | 28 | ||||
| -rw-r--r-- | frontends/ast/ast.h | 28 | ||||
| -rw-r--r-- | frontends/ast/dpicall.cc | 17 | ||||
| -rw-r--r-- | frontends/ast/genrtlil.cc | 91 | ||||
| -rw-r--r-- | frontends/ast/simplify.cc | 593 | 
5 files changed, 465 insertions, 292 deletions
| diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 1c0a8b34d..57552d86c 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -287,8 +287,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) const  	}  	std::string type_name = type2str(type); -	fprintf(f, "%s%s <%s:%d.%d-%d.%d>", indent.c_str(), type_name.c_str(), filename.c_str(), location.first_line, -		location.first_column, location.last_line, location.last_column); +	fprintf(f, "%s%s <%s>", indent.c_str(), type_name.c_str(), loc_string().c_str());  	if (!flag_no_dump_ptr) {  		if (id2ast) @@ -548,9 +547,9 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const  		break;  	case AST_CASE: -		if (!children.empty() && children[0]->type == AST_CONDX) +		if (children.size() > 1 && children[1]->type == AST_CONDX)  			fprintf(f, "%s" "casex (", indent.c_str()); -		else if (!children.empty() && children[0]->type == AST_CONDZ) +		else if (children.size() > 1 && children[1]->type == AST_CONDZ)  			fprintf(f, "%s" "casez (", indent.c_str());  		else  			fprintf(f, "%s" "case (", indent.c_str()); @@ -959,6 +958,16 @@ RTLIL::Const AstNode::realAsConst(int width)  	return result;  } +std::string AstNode::loc_string() const +{ +	return stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +} + +void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) +{ +	obj->attributes[ID::src] = ast->loc_string(); +} +  // create a new AstModule from an AST_MODULE AST node  static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)  { @@ -974,8 +983,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast  	current_module = new AstModule;  	current_module->ast = NULL;  	current_module->name = ast->str; -	current_module->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, -		ast->location.first_column, ast->location.last_line, ast->location.last_column); +	set_src_attr(current_module, ast);  	current_module->set_bool_attribute(ID::cells_not_processed);  	current_ast_mod = ast; @@ -1229,13 +1237,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump  				if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) {  					log_file_error((*it)->filename, (*it)->location.first_line, "Re-definition of module `%s'!\n", (*it)->str.c_str());  				} else if (nooverwrite) { -					log("Ignoring re-definition of module `%s' at %s:%d.%d-%d.%d.\n", -							(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); +					log("Ignoring re-definition of module `%s' at %s.\n", +							(*it)->str.c_str(), (*it)->loc_string().c_str());  					continue;  				} else { -					log("Replacing existing%s module `%s' at %s:%d.%d-%d.%d.\n", +					log("Replacing existing%s module `%s' at %s.\n",  							existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", -							(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); +							(*it)->str.c_str(), (*it)->loc_string().c_str());  					design->remove(existing_mod);  				}  			} diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 907392166..1c9a6ee47 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -252,8 +252,8 @@ namespace AST  		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); +		void expand_genblock(const std::string &prefix); +		void label_genblks(std::set<std::string>& existing, int &counter);  		void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,  				dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);  		bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); @@ -263,14 +263,22 @@ namespace AST  		bool detect_latch(const std::string &var);  		// 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); +		struct varinfo_t { +			RTLIL::Const val; +			int offset; +			bool is_signed; +			AstNode *arg = nullptr; +			bool explicitly_sized; +		}; +		bool has_const_only_constructs(); +		bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); +		AstNode *eval_const_function(AstNode *fcall, bool must_succeed);  		bool is_simple_const_expr();  		std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); +		bool is_recursive_function() const; +		std::pair<AstNode*, AstNode*> get_tern_choice(); +  		// create a human-readable text representation of the AST (for debugging)  		void dumpAst(FILE *f, std::string indent) const;  		void dumpVlog(FILE *f, std::string indent) const; @@ -315,6 +323,9 @@ namespace AST  		// helpers for enum  		void allocateDefaultEnumValues();  		void annotateTypedEnums(AstNode *template_node); + +		// helpers for locations +		std::string loc_string() const;  	};  	// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code @@ -353,6 +364,9 @@ namespace AST  	std::pair<std::string,std::string> split_modport_from_type(std::string name_type);  	AstNode * find_modport(AstNode *intf, std::string name);  	void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport); + +	// Helper for setting the src attribute. +	void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);  }  namespace AST_INTERNAL diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e241142d3..948c9083c 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -67,7 +67,7 @@ static ffi_fptr resolve_fn (std::string symbol_name)  AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args)  {  	AST::AstNode *newNode = nullptr; -	union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; +	union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1];  	ffi_type *types [args.size() + 1];  	void *values [args.size() + 1];  	ffi_cif cif; @@ -92,6 +92,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,  			value_store[i].i32 = args[i]->asInt(args[i]->is_signed);  			values[i] = &value_store[i].i32;  			types[i] = &ffi_type_sint32; +		} else if (argtypes[i] == "chandle") { +			log("  arg %d (%s): %llx\n", i, argtypes[i].c_str(), (unsigned long long)args[i]->asInt(false)); +			value_store[i].ptr = (void *)args[i]->asInt(args[i]->is_signed); +			values[i] = &value_store[i].ptr; +			types[i] = &ffi_type_pointer;  		} else {  			log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i);  		} @@ -106,6 +111,9 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,          } else if (rtype == "real") {                  types[args.size()] = &ffi_type_double;                  values[args.size()] = &value_store[args.size()].f64; +        } else if (rtype == "chandle") { +                types[args.size()] = &ffi_type_pointer; +                values[args.size()] = &value_store[args.size()].ptr;          } else {                  log_error("invalid rtype '%s'.\n", rtype.c_str());          } @@ -123,6 +131,13 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,  		newNode = new AstNode(AST_REALVALUE);  		newNode->realvalue = value_store[args.size()].f32;  		log("  return realvalue: %g\n", newNode->asReal(true)); +	} else if (rtype == "chandle") { +		uint64_t rawval = (uint64_t)value_store[args.size()].ptr; +		std::vector<RTLIL::State> bits(64); +		for (int i = 0; i < 64; i++) +			bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0; +		newNode = AstNode::mkconst_bits(bits, false); +		log("  return chandle: %llx\n", (unsigned long long)newNode->asInt(false));  	} else {  		newNode = AstNode::mkconst_int(value_store[args.size()].i32, false);  		log("  return integer: %lld\n", (long long)newNode->asInt(true)); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index b8bfdf65e..449f8c38e 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -45,10 +45,11 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width  {  	IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++);  	RTLIL::Cell *cell = current_module->addCell(name, type); -	cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(cell, that);  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); -	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(wire, that); +	wire->is_signed = that->is_signed;  	if (gen_attributes)  		for (auto &attr : that->attributes) { @@ -76,10 +77,11 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s  	IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++);  	RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); -	cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(cell, that);  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); -	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(wire, that); +	wire->is_signed = that->is_signed;  	if (that != NULL)  		for (auto &attr : that->attributes) { @@ -102,10 +104,10 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width  {  	IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++);  	RTLIL::Cell *cell = current_module->addCell(name, type); -	cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(cell, that);  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); -	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(wire, that);  	wire->is_signed = that->is_signed;  	for (auto &attr : that->attributes) { @@ -137,10 +139,10 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const  	sstr << "$ternary$" << that->filename << ":" << that->location.first_line << "$" << (autoidx++);  	RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); -	cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(cell, that);  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); -	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	set_src_attr(wire, that);  	wire->is_signed = that->is_signed;  	for (auto &attr : that->attributes) { @@ -318,7 +320,7 @@ struct AST_INTERNAL::ProcessGenerator  		// generate process and simple root case  		proc = new RTLIL::Process; -		proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); +		set_src_attr(proc, always);  		proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++);  		for (auto &attr : always->attributes) {  			if (attr.second->type != AST_CONSTANT) @@ -354,7 +356,7 @@ struct AST_INTERNAL::ProcessGenerator  		if (found_anyedge_syncs) {  			if (found_global_syncs)  				log_file_error(always->filename, always->location.first_line, "Found non-synthesizable event list!\n"); -			log("Note: Assuming pure combinatorial block at %s:%d.%d-%d.%d in\n", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); +			log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string().c_str());  			log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");  			log("use of @* instead of @(...) for better match of synthesis and simulation.\n");  		} @@ -454,7 +456,7 @@ struct AST_INTERNAL::ProcessGenerator  			} while (current_module->wires_.count(wire_name) > 0);  			RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); -			wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); +			set_src_attr(wire, always);  			chunk.wire = wire;  			chunk.offset = 0; @@ -589,7 +591,7 @@ struct AST_INTERNAL::ProcessGenerator  		case AST_CASE:  			{  				RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; -				sw->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, ast->location.first_column, ast->location.last_line, ast->location.last_column); +				set_src_attr(sw, ast);  				sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());  				current_case->switches.push_back(sw); @@ -623,7 +625,7 @@ struct AST_INTERNAL::ProcessGenerator  					RTLIL::CaseRule *backup_case = current_case;  					current_case = new RTLIL::CaseRule; -					current_case->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", child->filename.c_str(), child->location.first_line, child->location.first_column, child->location.last_line, child->location.last_column); +					set_src_attr(current_case, child);  					last_generated_case = current_case;  					addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);  					for (auto node : child->children) { @@ -942,6 +944,41 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun  			}  			break;  		} +		if (current_scope.count(str)) +		{ +			// This width detection is needed for function calls which are +			// unelaborated, which currently only applies to calls to recursive +			// functions reached by unevaluated ternary branches. +			const AstNode *func = current_scope.at(str); +			if (func->type != AST_FUNCTION) +				log_file_error(filename, location.first_line, "Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str()); +			const AstNode *wire = nullptr; +			for (const AstNode *child : func->children) +				if (child->str == func->str) { +					wire = child; +					break; +				} +			log_assert(wire && wire->type == AST_WIRE); +			sign_hint = wire->is_signed; +			width_hint = 1; +			if (!wire->children.empty()) +			{ +				log_assert(wire->children.size() == 1); +				const AstNode *range = wire->children.at(0); +				log_assert(range->type == AST_RANGE && range->children.size() == 2); +				AstNode *left = range->children.at(0)->clone(); +				AstNode *right = range->children.at(1)->clone(); +				while (left->simplify(true, false, false, 1, -1, false, true)) { } +				while (right->simplify(true, false, false, 1, -1, false, true)) { } +				if (left->type != AST_CONSTANT || right->type != AST_CONSTANT) +					log_file_error(filename, location.first_line, "Function %s has non-constant width!", +							RTLIL::unescape_id(str).c_str()); +				width_hint = abs(int(left->asInt(true) - right->asInt(true))); +				delete left; +				delete right; +			} +			break; +		}  		YS_FALLTHROUGH  	// everything should have been handled above -> print error if not. @@ -1011,7 +1048,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  		// This is used by the hierarchy pass to know when it can replace interface connection with the individual  		// signals.  		RTLIL::Wire *wire = current_module->addWire(str, 1); -		wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +		set_src_attr(wire, this);  		wire->start_offset = 0;  		wire->port_id = port_id;  		wire->port_input = true; @@ -1050,8 +1087,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			RTLIL::Const val = children[0]->bitsAsConst();  			RTLIL::Wire *wire = current_module->addWire(str, GetSize(val));  			current_module->connect(wire, val); +			wire->is_signed = children[0]->is_signed; -			wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(wire, this);  			wire->attributes[type == AST_PARAMETER ? ID::parameter : ID::localparam] = 1;  			for (auto &attr : attributes) { @@ -1073,7 +1111,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				log_file_error(filename, location.first_line, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1);  			RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); -			wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(wire, this);  			wire->start_offset = range_right;  			wire->port_id = port_id;  			wire->port_input = is_input; @@ -1105,7 +1143,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				log_file_error(filename, location.first_line, "Memory `%s' with non-constant width or size!\n", str.c_str());  			RTLIL::Memory *memory = new RTLIL::Memory; -			memory->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(memory, this);  			memory->name = str;  			memory->width = children[0]->range_left - children[0]->range_right + 1;  			if (children[1]->range_right < children[1]->range_left) { @@ -1162,7 +1200,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {  				RTLIL::Wire *wire = current_module->addWire(str); -				wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +				set_src_attr(wire, this);  				wire->name = str;  				if (flag_autowire)  					log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str()); @@ -1544,13 +1582,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			sstr << "$memrd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);  			RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); -			cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(cell, this);  			RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); -			wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(wire, this);  			int mem_width, mem_size, addr_bits;  			is_signed = id2ast->is_signed; +			wire->is_signed = is_signed;  			id2ast->meminfo(mem_width, mem_size, addr_bits);  			RTLIL::SigSpec addr_sig = children[0]->genRTLIL(); @@ -1582,7 +1621,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);  			RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit)); -			cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(cell, this);  			int mem_width, mem_size, addr_bits;  			id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1646,7 +1685,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				cellname = str;  			RTLIL::Cell *cell = current_module->addCell(cellname, celltype); -			cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(cell, this);  			for (auto &attr : attributes) {  				if (attr.second->type != AST_CONSTANT) @@ -1691,7 +1730,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				log_file_error(filename, location.first_line, "Re-definition of cell `%s'!\n", str.c_str());  			RTLIL::Cell *cell = current_module->addCell(str, ""); -			cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +			set_src_attr(cell, this);  			// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass  			cell->set_bool_attribute(ID::module_not_derived); @@ -1740,7 +1779,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  							// non-trivial signed nodes are indirected through  							// signed wires to enable sign extension  							RTLIL::IdString wire_name = NEW_ID; -							RTLIL::Wire *wire = current_module->addWire(wire_name, arg->bits.size()); +							RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig));  							wire->is_signed = true;  							current_module->connect(wire, sig);  							sig = wire; @@ -1855,7 +1894,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  					log_file_error(filename, location.first_line, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str());  				Cell *cell = current_module->addCell(myid, str.substr(1)); -				cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +				set_src_attr(cell, this);  				cell->parameters[ID::WIDTH] = width;  				if (attributes.count(ID::reg)) { @@ -1866,7 +1905,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				}  				Wire *wire = current_module->addWire(myid + "_wire", width); -				wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +				set_src_attr(wire, this);  				cell->setPort(ID::Y, wire);  				is_signed = sign_hint; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index fc2976c83..6b4b9e045 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var)  	return true;  } +static std::string prefix_id(const std::string &prefix, const std::string &str) +{ +	log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); +	log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); +	log_assert(prefix.back() == '.'); +	if (str.front() == '\\') +		return prefix + str.substr(1); +	return prefix + str; +} +  // 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(). @@ -565,6 +575,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		deep_recursion_warning = false;  	} +	static bool unevaluated_tern_branch = false; +  	AstNode *newNode = NULL;  	bool did_something = false; @@ -748,6 +760,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	// also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")  	if (type == AST_MODULE) {  		current_scope.clear(); +		std::set<std::string> existing; +		int counter = 0; +		label_genblks(existing, counter);  		std::map<std::string, AstNode*> this_wire_scope;  		for (size_t i = 0; i < children.size(); i++) {  			AstNode *node = children[i]; @@ -928,7 +943,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			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 asignment  			if ((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.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +				log_warning("wire '%s' is assigned in a block at %s.\n", children[0]->str.c_str(), loc_string().c_str());  			if (type == AST_ASSIGN && children[0]->id2ast->is_reg) {  				bool is_rand_reg = false;  				if (children[1]->type == AST_FCALL) { @@ -942,7 +957,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  						is_rand_reg = true;  				}  				if (!is_rand_reg) -					log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +					log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", children[0]->str.c_str(), loc_string().c_str());  			}  			children[0]->was_checked = true;  		} @@ -1078,7 +1093,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		break;  	case AST_TERNARY: -		detect_width_simple = true;  		child_0_is_self_determined = true;  		break; @@ -1111,6 +1125,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		detectSignWidth(width_hint, sign_hint);  	if (type == AST_TERNARY) { +		if (width_hint < 0) { +			while (!children[0]->basic_prep && children[0]->simplify(true, false, in_lvalue, stage, -1, false, in_param)) +				did_something = true; + +			bool backup_unevaluated_tern_branch = unevaluated_tern_branch; +			AstNode *chosen = get_tern_choice().first; + +			unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2]; +			while (!children[1]->basic_prep && children[1]->simplify(false, false, in_lvalue, stage, -1, false, in_param)) +				did_something = true; + +			unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1]; +			while (!children[2]->basic_prep && children[2]->simplify(false, false, in_lvalue, stage, -1, false, in_param)) +				did_something = true; + +			unevaluated_tern_branch = backup_unevaluated_tern_branch; +			detectSignWidth(width_hint, sign_hint); +		}  		int width_hint_left, width_hint_right;  		bool sign_hint_left, sign_hint_right;  		bool found_real_left, found_real_right; @@ -1174,6 +1206,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	for (size_t i = 0; i < children.size(); i++) {  		bool did_something_here = true;  		bool backup_flag_autowire = flag_autowire; +		bool backup_unevaluated_tern_branch = unevaluated_tern_branch;  		if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)  			break;  		if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1) @@ -1186,6 +1219,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			break;  		if (type == AST_DEFPARAM && i == 0)  			flag_autowire = true; +		if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) { +			AstNode *chosen = get_tern_choice().first; +			unevaluated_tern_branch = chosen && chosen != children[i]; +		}  		while (did_something_here && i < children.size()) {  			bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;  			int width_hint_here = width_hint; @@ -1205,11 +1242,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				current_block = this;  				current_block_child = children[i];  			} -			if (!in_param_here && type == AST_FCALL) { -				bool recommend_const_eval = false; -				bool require_const_eval = has_const_only_constructs(recommend_const_eval); -				in_param_here = recommend_const_eval || require_const_eval; -			}  			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) @@ -1230,6 +1262,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			did_something = true;  		}  		flag_autowire = backup_flag_autowire; +		unevaluated_tern_branch = backup_unevaluated_tern_branch;  	}  	for (auto &attr : attributes) {  		while (attr.second->simplify(true, false, false, stage, -1, false, true)) @@ -1855,19 +1888,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			// expand body  			int index = varbuf->children[0]->integer; -			if (body_ast->type == AST_GENBLOCK) -				buf = body_ast->clone(); -			else -				buf = new AstNode(AST_GENBLOCK, body_ast->clone()); -			if (buf->str.empty()) { -				std::stringstream sstr; -				sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++); -				buf->str = sstr.str(); -			} -			std::map<std::string, std::string> name_map; +			log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); +			log_assert(!body_ast->str.empty()); +			buf = body_ast->clone(); +  			std::stringstream sstr;  			sstr << buf->str << "[" << index << "]."; -			buf->expand_genblock(varbuf->str, sstr.str(), name_map); +			std::string prefix = sstr.str(); + +			// create a scoped localparam for the current value of the loop variable +			AstNode *local_index = varbuf->clone(); +			size_t pos = local_index->str.rfind('.'); +			if (pos != std::string::npos) // remove outer prefix +				local_index->str = "\\" + local_index->str.substr(pos + 1); +			local_index->str = prefix_id(prefix, local_index->str); +			current_scope[local_index->str] = local_index; +			current_ast_mod->children.push_back(local_index); + +			buf->expand_genblock(prefix);  			if (type == AST_GENFOR) {  				for (size_t i = 0; i < buf->children.size(); i++) { @@ -1915,14 +1953,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	{  		for (size_t i = 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]->type == AST_TYPEDEF) -				log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); +			{ +				log_assert(!VERILOG_FRONTEND::sv_mode); +				log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); +			}  	}  	// transform block with name  	if (type == AST_BLOCK && !str.empty())  	{ -		std::map<std::string, std::string> name_map; -		expand_genblock(std::string(), str + ".", name_map); +		expand_genblock(str + ".");  		std::vector<AstNode*> new_children;  		for (size_t i = 0; i < children.size(); i++) @@ -1942,8 +1982,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_GENBLOCK && children.size() != 0)  	{  		if (!str.empty()) { -			std::map<std::string, std::string> name_map; -			expand_genblock(std::string(), str + ".", name_map); +			expand_genblock(str + ".");  		}  		for (size_t i = 0; i < children.size(); i++) { @@ -1979,8 +2018,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				buf = new AstNode(AST_GENBLOCK, buf);  			if (!buf->str.empty()) { -				std::map<std::string, std::string> name_map; -				buf->expand_genblock(std::string(), buf->str + ".", name_map); +				buf->expand_genblock(buf->str + ".");  			}  			for (size_t i = 0; i < buf->children.size(); i++) { @@ -2058,8 +2096,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			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); +				buf->expand_genblock(buf->str + ".");  			}  			for (size_t i = 0; i < buf->children.size(); i++) { @@ -3159,16 +3196,21 @@ skip_dynamic_range_lvalue_expansion:;  				log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str());  		} -		AstNode *decl = current_scope[str];  		std::stringstream sstr; -		sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; +		sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (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) && !decl->attributes.count(ID::via_celltype)) +		AstNode *decl = current_scope[str]; +		if (unevaluated_tern_branch && decl->is_recursive_function()) +			goto replace_fcall_later; +		decl = decl->clone(); +		decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion +		decl->expand_genblock(prefix); + +		if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype))  		{ +			bool require_const_eval = decl->has_const_only_constructs();  			bool all_args_const = true;  			for (auto child : children) {  				while (child->simplify(true, false, false, 1, -1, false, true)) { } @@ -3177,12 +3219,14 @@ 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); +				AstNode *func_workspace = decl->clone(); +				func_workspace->str = prefix_id(prefix, "$result"); +				newNode = func_workspace->eval_const_function(this, in_param || require_const_eval);  				delete func_workspace; -				goto apply_newNode; +				if (newNode) { +					delete decl; +					goto apply_newNode; +				}  			}  			if (in_param) @@ -3192,8 +3236,6 @@ skip_dynamic_range_lvalue_expansion:;  		}  		size_t arg_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; @@ -3203,16 +3245,17 @@ skip_dynamic_range_lvalue_expansion:;  			log_assert(type == AST_FCALL);  			AstNode *wire = NULL; +			std::string res_name = prefix_id(prefix, "$result");  			for (auto child : decl->children) -				if (child->type == AST_WIRE && child->str == str) +				if (child->type == AST_WIRE && child->str == res_name)  					wire = child->clone();  			log_assert(wire != NULL); -			wire->str = prefix + str;  			wire->port_id = 0;  			wire->is_input = false;  			wire->is_output = false; +			current_scope[wire->str] = wire;  			current_ast_mod->children.push_back(wire);  			while (wire->simplify(true, false, false, 1, -1, false, false)) { } @@ -3256,7 +3299,6 @@ skip_dynamic_range_lvalue_expansion:;  				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; @@ -3318,7 +3360,6 @@ skip_dynamic_range_lvalue_expansion:;  				else  				{  					wire = child->clone(); -					wire->str = prefix + wire->str;  					wire->port_id = 0;  					wire->is_input = false;  					wire->is_output = false; @@ -3329,15 +3370,11 @@ skip_dynamic_range_lvalue_expansion:;  					wire_cache[child->str] = wire; +					current_scope[wire->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; +				while (wire->simplify(true, false, false, 1, -1, false, false)) { }  				if ((child->is_input || child->is_output) && arg_count < children.size())  				{ @@ -3366,6 +3403,8 @@ skip_dynamic_range_lvalue_expansion:;  								range->children.push_back(mkconst_int(0, true));  							}  						} +						// updates the sizing +						while (wire->simplify(true, false, false, 1, -1, false, false)) { }  						continue;  					}  					AstNode *wire_id = new AstNode(AST_IDENTIFIER); @@ -3381,18 +3420,9 @@ skip_dynamic_range_lvalue_expansion:;  				}  			} -		for (auto child : added_mod_children) { -			child->replace_ids(prefix, replace_rules); -			while (child->simplify(true, false, false, 1, -1, false, false)) { } -		} -  		for (auto child : 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.push_back(child->clone());  		new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); @@ -3405,10 +3435,11 @@ skip_dynamic_range_lvalue_expansion:;  		}  	replace_fcall_with_id: +		delete decl;  		if (type == AST_FCALL) {  			delete_children();  			type = AST_IDENTIFIER; -			str = prefix + str; +			str = prefix_id(prefix, "$result");  		}  		if (type == AST_TCALL)  			str = ""; @@ -3608,24 +3639,9 @@ replace_fcall_later:;  		case AST_TERNARY:  			if (children[0]->isConst())  			{ -				bool found_sure_true = false; -				bool found_maybe_true = false; - -				if (children[0]->type == AST_CONSTANT) -					for (auto &bit : children[0]->bits) { -						if (bit == RTLIL::State::S1) -							found_sure_true = true; -						if (bit > RTLIL::State::S1) -							found_maybe_true = true; -					} -				else -					found_sure_true = children[0]->asReal(sign_hint) != 0; - -				AstNode *choice = NULL, *not_choice = NULL; -				if (found_sure_true) -					choice = children[1], not_choice = children[2]; -				else if (!found_maybe_true) -					choice = children[2], not_choice = children[1]; +				auto pair = get_tern_choice(); +				AstNode *choice = pair.first; +				AstNode *not_choice = pair.second;  				if (choice != NULL) {  					if (choice->type == AST_CONSTANT) { @@ -3859,63 +3875,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m  	return block;  } -// annotate the names of all wires and other named objects in a generate block -void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) +// annotate the names of all wires and other named objects in a named generate +// or procedural block; nested blocks are themselves annotated such that the +// prefix is carried forward, but resolution of their children is deferred +void AstNode::expand_genblock(const std::string &prefix)  { -	// `original_scope` defaults to false, and is used to prevent the premature -	// prefixing of items in named sub-blocks - -	if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { -		if (children.empty()) { -			current_scope[index_var]->children[0]->cloneInto(this); -		} else { -			AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); -			p->str = stringf("$genval$%d", autoidx++); -			current_ast_mod->children.push_back(p); -			str = p->str; -			id2ast = p; -		} -	} -  	if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { -		if (name_map.count(str) > 0) { -			str = name_map[str]; -		} else { -			// remap the prefix of this ident if it is a local generate scope -			size_t pos = str.rfind('.'); -			if (pos != std::string::npos) { -				std::string existing_prefix = str.substr(0, pos); -				if (name_map.count(existing_prefix) > 0) { -					str = name_map[existing_prefix] + str.substr(pos); -				} +		log_assert(!str.empty()); + +		// search starting in the innermost scope and then stepping outward +		for (size_t ppos = prefix.size() - 1; ppos; --ppos) { +			if (prefix.at(ppos) != '.') continue; + +			std::string new_prefix = prefix.substr(0, ppos + 1); +			auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { +				std::string new_name = prefix_id(new_prefix, ident); +				if (current_scope.count(new_name)) +					return new_name; +				return {}; +			}; + +			// attempt to resolve the full identifier +			std::string resolved = attempt_resolve(str); +			if (!resolved.empty()) { +				str = resolved; +				break;  			} -		} -	} - -	std::map<std::string, std::string> backup_name_map; -	auto prefix_node = [&](AstNode* child) { -		if (backup_name_map.size() == 0) -			backup_name_map = name_map; +			// attempt to resolve hierarchical prefixes within the identifier, +			// as the prefix could refer to a local scope which exists but +			// hasn't yet been elaborated +			for (size_t spos = str.size() - 1; spos; --spos) { +				if (str.at(spos) != '.') continue; +				resolved = attempt_resolve(str.substr(0, spos)); +				if (!resolved.empty()) { +					str = resolved + str.substr(spos); +					ppos = 1; // break outer loop +					break; +				} +			} -		// if within a nested scope -		if (!original_scope) { -			// this declaration shadows anything in the parent scope(s) -			name_map[child->str] = child->str; -			return;  		} +	} -		std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; -		size_t pos = child->str.rfind('.'); -		if (pos == std::string::npos) -			pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; -		else -			pos = pos + 1; -		new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); -		if (new_name[0] != '$' && new_name[0] != '\\') -			new_name = prefix[0] + new_name; - -		name_map[child->str] = new_name; +	auto prefix_node = [&prefix](AstNode* child) { +		if (child->str.empty()) return; +		std::string new_name = prefix_id(prefix, child->str);  		if (child->type == AST_FUNCTION)  			child->replace_result_wire_name_in_function(child->str, new_name);  		else @@ -3967,43 +3972,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma  			continue;  		// functions/tasks may reference wires, constants, etc. in this scope  		if (child->type == AST_FUNCTION || child->type == AST_TASK) -			child->expand_genblock(index_var, prefix, name_map, false); -		// continue prefixing if this child block is anonymous -		else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) -			child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); -		else -			child->expand_genblock(index_var, prefix, name_map, original_scope); -	} - +			continue; +		// named blocks pick up the current prefix and will expanded later +		if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) +			continue; -	if (backup_name_map.size() > 0) -		name_map.swap(backup_name_map); +		child->expand_genblock(prefix); +	}  } -// rename stuff (used when tasks of functions are instantiated) -void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) +// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or +// IEEE 1800-2017 Section 27.6 +void AstNode::label_genblks(std::set<std::string>& existing, int &counter)  { -	if (type == AST_BLOCK) -	{ -		std::map<std::string, std::string> new_rules = rules; -		std::string new_prefix = prefix + str; - -		for (auto child : children) -			if (child->type == AST_WIRE) { -				new_rules[child->str] = new_prefix + child->str; -				child->str = new_prefix + child->str; -			} +	switch (type) { +	case AST_GENIF: +	case AST_GENFOR: +	case AST_GENCASE: +		// seeing a proper generate control flow construct increments the +		// counter once +		++counter; +		for (AstNode *child : children) +			child->label_genblks(existing, counter); +		break; -		for (auto child : children) -			if (child->type != AST_WIRE) -				child->replace_ids(new_prefix, new_rules); +	case AST_GENBLOCK: { +		// if this block is unlabeled, generate its corresponding unique name +		for (int padding = 0; str.empty(); ++padding) { +			std::string candidate = "\\genblk"; +			for (int i = 0; i < padding; ++i) +				candidate += '0'; +			candidate += std::to_string(counter); +			if (!existing.count(candidate)) +				str = candidate; +		} +		// within a genblk, the counter starts fresh +		std::set<std::string> existing_local = existing; +		int counter_local = 0; +		for (AstNode *child : children) +			child->label_genblks(existing_local, counter_local); +		break;  	} -	else -	{ -		if (type == AST_IDENTIFIER && rules.count(str) > 0) -			str = rules.at(str); -		for (auto child : children) -			child->replace_ids(prefix, rules); + +	default: +		// track names which could conflict with implicit genblk names +		if (str.rfind("\\genblk", 0) == 0) +			existing.insert(str); +		for (AstNode *child : children) +			child->label_genblks(existing, counter); +		break;  	}  } @@ -4495,33 +4512,12 @@ 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) +bool AstNode::has_const_only_constructs()  { -	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(visited, recommend_const_eval)) -			return true;  	for (auto child : children) -		if (child->AstNode::has_const_only_constructs(visited, recommend_const_eval)) +		if (child->has_const_only_constructs())  			return true;  	return false;  } @@ -4537,19 +4533,26 @@ bool AstNode::is_simple_const_expr()  }  // helper function for AstNode::eval_const_function() -void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) +bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed)  {  	if (type == AST_IDENTIFIER && variables.count(str)) {  		int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();  		if (!children.empty()) { -			if (children.size() != 1 || children.at(0)->type != AST_RANGE) -				log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); -			children.at(0)->replace_variables(variables, fcall); +			if (children.size() != 1 || children.at(0)->type != AST_RANGE) { +				if (!must_succeed) +					return false; +				log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s: ...called from here.\n", +						fcall->loc_string().c_str()); +			} +			if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) +				return false;  			while (simplify(true, false, false, 1, -1, false, true)) { } -			if (!children.at(0)->range_valid) -				log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			if (!children.at(0)->range_valid) { +				if (!must_succeed) +					return false; +				log_file_error(filename, location.first_line, "Non-constant range\n%s: ... called from here.\n", +						fcall->loc_string().c_str()); +			}  			offset = min(children.at(0)->range_left, children.at(0)->range_right);  			width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);  		} @@ -4559,19 +4562,22 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia  		AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);  		newNode->cloneInto(this);  		delete newNode; -		return; +		return true;  	}  	for (auto &child : children) -		child->replace_variables(variables, fcall); +		if (!child->replace_variables(variables, fcall, must_succeed)) +			return false; +	return true;  } -// evaluate functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall) +// attempt to statically evaluate a functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)  { -	std::map<std::string, AstNode*> backup_scope; +	std::map<std::string, AstNode*> backup_scope = current_scope;  	std::map<std::string, AstNode::varinfo_t> variables;  	AstNode *block = new AstNode(AST_BLOCK); +	AstNode *result = nullptr;  	size_t argidx = 0;  	for (auto child : children) @@ -4593,24 +4599,37 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_WIRE)  		{  			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } -			if (!stmt->range_valid) -				log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", -						stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); -			variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); -			variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); -			variables[stmt->str].is_signed = stmt->is_signed; +			if (!stmt->range_valid) { +				if (!must_succeed) +					goto finished; +				log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s: ... called from here.\n", +						stmt->str.c_str(), fcall->loc_string().c_str()); +			} +			AstNode::varinfo_t &variable = variables[stmt->str]; +			int width = abs(stmt->range_left - stmt->range_right) + 1; +			// if this variable has already been declared as an input, check the +			// sizes match if it already had an explicit size +			if (variable.arg && variable.explicitly_sized && variable.val.size() != width) { +				log_file_error(filename, location.first_line, "Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str()); +			} +			variable.val = RTLIL::Const(RTLIL::State::Sx, width); +			variable.offset = min(stmt->range_left, stmt->range_right); +			variable.is_signed = stmt->is_signed; +			variable.explicitly_sized = stmt->children.size() && +				stmt->children.back()->type == AST_RANGE; +			// identify the argument corresponding to this wire, if applicable  			if (stmt->is_input && argidx < fcall->children.size()) { -				int width = variables[stmt->str].val.bits.size(); -				auto* arg_node = fcall->children.at(argidx++); -				if (arg_node->type == AST_CONSTANT) { -					variables[stmt->str].val = arg_node->bitsAsConst(width); +				variable.arg = fcall->children.at(argidx++); +			} +			// load the constant arg's value into this variable +			if (variable.arg) { +				if (variable.arg->type == AST_CONSTANT) { +					variable.val = variable.arg->bitsAsConst(width);  				} else { -					log_assert(arg_node->type == AST_REALVALUE); -					variables[stmt->str].val = arg_node->realAsConst(width); +					log_assert(variable.arg->type == AST_REALVALUE); +					variable.val = variable.arg->realAsConst(width);  				}  			} -			if (!backup_scope.count(stmt->str)) -				backup_scope[stmt->str] = current_scope[stmt->str];  			current_scope[stmt->str] = stmt;  			block->children.erase(block->children.begin()); @@ -4623,8 +4642,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		{  			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } -			if (!backup_scope.count(stmt->str)) -				backup_scope[stmt->str] = current_scope[stmt->str];  			current_scope[stmt->str] = stmt;  			block->children.erase(block->children.begin()); @@ -4635,32 +4652,46 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		{  			if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 &&  					stmt->children.at(0)->children.at(0)->type == AST_RANGE) -				stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); -			stmt->children.at(1)->replace_variables(variables, fcall); +				if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) +					goto finished; +			if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (stmt->simplify(true, false, false, 1, -1, false, true)) { }  			if (stmt->type != AST_ASSIGN_EQ)  				continue; -			if (stmt->children.at(1)->type != AST_CONSTANT) -				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			if (stmt->children.at(1)->type != AST_CONSTANT) { +				if (!must_succeed) +					goto finished; +				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here. X\n", +						fcall->loc_string().c_str()); +			} -			if (stmt->children.at(0)->type != AST_IDENTIFIER) -				log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			if (stmt->children.at(0)->type != AST_IDENTIFIER) { +				if (!must_succeed) +					goto finished; +				log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s: ... called from here.\n", +						fcall->loc_string().c_str()); +			} -			if (!variables.count(stmt->children.at(0)->str)) -				log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			if (!variables.count(stmt->children.at(0)->str)) { +				if (!must_succeed) +					goto finished; +				log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s: ... called from here.\n", +						fcall->loc_string().c_str()); +			}  			if (stmt->children.at(0)->children.empty()) {  				variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size());  			} else {  				AstNode *range = stmt->children.at(0)->children.at(0); -				if (!range->range_valid) -					log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", -							fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +				if (!range->range_valid) { +					if (!must_succeed) +						goto finished; +					log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s: ... called from here.\n", +							fcall->loc_string().c_str()); +				}  				int offset = min(range->range_left, range->range_right);  				int width = std::abs(range->range_left - range->range_right) + 1;  				varinfo_t &v = variables[stmt->children.at(0)->str]; @@ -4687,12 +4718,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_WHILE)  		{  			AstNode *cond = stmt->children.at(0)->clone(); -			cond->replace_variables(variables, fcall); +			if (!cond->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (cond->simplify(true, false, false, 1, -1, false, true)) { } -			if (cond->type != AST_CONSTANT) -				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			if (cond->type != AST_CONSTANT) { +				if (!must_succeed) +					goto finished; +				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", +						fcall->loc_string().c_str()); +			}  			if (cond->asBool()) {  				block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -4708,12 +4743,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_REPEAT)  		{  			AstNode *num = stmt->children.at(0)->clone(); -			num->replace_variables(variables, fcall); +			if (!num->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (num->simplify(true, false, false, 1, -1, false, true)) { } -			if (num->type != AST_CONSTANT) -				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", -						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			if (num->type != AST_CONSTANT) { +				if (!must_succeed) +					goto finished; +				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", +						fcall->loc_string().c_str()); +			}  			block->children.erase(block->children.begin());  			for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -4727,7 +4766,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_CASE)  		{  			AstNode *expr = stmt->children.at(0)->clone(); -			expr->replace_variables(variables, fcall); +			if (!expr->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (expr->simplify(true, false, false, 1, -1, false, true)) { }  			AstNode *sel_case = NULL; @@ -4744,14 +4784,18 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  				for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++)  				{  					AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); -					cond->replace_variables(variables, fcall); +					if (!cond->replace_variables(variables, fcall, must_succeed)) +						goto finished;  					cond = new AstNode(AST_EQ, expr->clone(), cond);  					while (cond->simplify(true, false, false, 1, -1, false, true)) { } -					if (cond->type != AST_CONSTANT) -						log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", -								fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +					if (cond->type != AST_CONSTANT) { +						if (!must_succeed) +							goto finished; +						log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", +								fcall->loc_string().c_str()); +					}  					found_match = cond->asBool();  					delete cond; @@ -4773,6 +4817,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_BLOCK)  		{ +			if (!stmt->str.empty()) +				stmt->expand_genblock(stmt->str + "."); +  			block->children.erase(block->children.begin());  			block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());  			stmt->children.clear(); @@ -4780,20 +4827,20 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  			continue;  		} -		log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", -				fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +		if (!must_succeed) +			goto finished; +		log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s: ... called from here.\n", +				fcall->loc_string().c_str());  		log_abort();  	} -	delete block; +	result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); -	for (auto &it : backup_scope) -		if (it.second == NULL) -			current_scope.erase(it.first); -		else -			current_scope[it.first] = it.second; +finished: +	delete block; +	current_scope = backup_scope; -	return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); +	return result;  }  void AstNode::allocateDefaultEnumValues() @@ -4824,4 +4871,54 @@ void AstNode::allocateDefaultEnumValues()  	}  } +bool AstNode::is_recursive_function() const +{ +	std::set<const AstNode *> visited; +	std::function<bool(const AstNode *node)> visit = [&](const AstNode *node) { +		if (visited.count(node)) +			return node == this; +		visited.insert(node); +		if (node->type == AST_FCALL) { +			auto it = current_scope.find(node->str); +			if (it != current_scope.end() && visit(it->second)) +				return true; +		} +		for (const AstNode *child : node->children) { +			if (visit(child)) +				return true; +		} +		return false; +	}; + +	log_assert(type == AST_FUNCTION); +	return visit(this); +} + +std::pair<AstNode*, AstNode*> AstNode::get_tern_choice() +{ +	if (!children[0]->isConst()) +		return {}; + +	bool found_sure_true = false; +	bool found_maybe_true = false; + +	if (children[0]->type == AST_CONSTANT) +		for (auto &bit : children[0]->bits) { +			if (bit == RTLIL::State::S1) +				found_sure_true = true; +			if (bit > RTLIL::State::S1) +				found_maybe_true = true; +		} +	else +		found_sure_true = children[0]->asReal(true) != 0; + +	AstNode *choice = nullptr, *not_choice = nullptr; +	if (found_sure_true) +		choice = children[1], not_choice = children[2]; +	else if (!found_maybe_true) +		choice = children[2], not_choice = children[1]; + +	return {choice, not_choice}; +} +  YOSYS_NAMESPACE_END | 
