mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +00:00 
			
		
		
		
	Merge pull request #2529 from zachjs/unnamed-genblk
verilog: significant block scoping improvements
This commit is contained in:
		
						commit
						baf1875307
					
				
					 33 changed files with 783 additions and 262 deletions
				
			
		|  | @ -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); | 		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); | 		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); | 		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 expand_genblock(const std::string &prefix); | ||||||
| 		void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); | 		void label_genblks(std::set<std::string>& existing, int &counter); | ||||||
| 		void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, | 		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); | 				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); | 		bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); | ||||||
|  |  | ||||||
|  | @ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) | ||||||
| 	return true; | 	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
 | // 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
 | // values, unrolled for-loops, expanded generate blocks, etc. when this function
 | ||||||
| // is done with an AST it can be converted into RTLIL using genRTLIL().
 | // is done with an AST it can be converted into RTLIL using genRTLIL().
 | ||||||
|  | @ -748,6 +758,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;")
 | 	// also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")
 | ||||||
| 	if (type == AST_MODULE) { | 	if (type == AST_MODULE) { | ||||||
| 		current_scope.clear(); | 		current_scope.clear(); | ||||||
|  | 		std::set<std::string> existing; | ||||||
|  | 		int counter = 0; | ||||||
|  | 		label_genblks(existing, counter); | ||||||
| 		std::map<std::string, AstNode*> this_wire_scope; | 		std::map<std::string, AstNode*> this_wire_scope; | ||||||
| 		for (size_t i = 0; i < children.size(); i++) { | 		for (size_t i = 0; i < children.size(); i++) { | ||||||
| 			AstNode *node = children[i]; | 			AstNode *node = children[i]; | ||||||
|  | @ -1855,19 +1868,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 
 | 
 | ||||||
| 			// expand body
 | 			// expand body
 | ||||||
| 			int index = varbuf->children[0]->integer; | 			int index = varbuf->children[0]->integer; | ||||||
| 			if (body_ast->type == AST_GENBLOCK) | 			log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); | ||||||
| 				buf = body_ast->clone(); | 			log_assert(!body_ast->str.empty()); | ||||||
| 			else | 			buf = body_ast->clone(); | ||||||
| 				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; |  | ||||||
| 			std::stringstream sstr; | 			std::stringstream sstr; | ||||||
| 			sstr << buf->str << "[" << index << "]."; | 			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) { | 			if (type == AST_GENFOR) { | ||||||
| 				for (size_t i = 0; i < buf->children.size(); i++) { | 				for (size_t i = 0; i < buf->children.size(); i++) { | ||||||
|  | @ -1915,14 +1933,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 	{ | 	{ | ||||||
| 		for (size_t i = 0; i < children.size(); i++) | 		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) | 			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
 | 	// transform block with name
 | ||||||
| 	if (type == AST_BLOCK && !str.empty()) | 	if (type == AST_BLOCK && !str.empty()) | ||||||
| 	{ | 	{ | ||||||
| 		std::map<std::string, std::string> name_map; | 		expand_genblock(str + "."); | ||||||
| 		expand_genblock(std::string(), str + ".", name_map); |  | ||||||
| 
 | 
 | ||||||
| 		std::vector<AstNode*> new_children; | 		std::vector<AstNode*> new_children; | ||||||
| 		for (size_t i = 0; i < children.size(); i++) | 		for (size_t i = 0; i < children.size(); i++) | ||||||
|  | @ -1942,8 +1962,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 	if (type == AST_GENBLOCK && children.size() != 0) | 	if (type == AST_GENBLOCK && children.size() != 0) | ||||||
| 	{ | 	{ | ||||||
| 		if (!str.empty()) { | 		if (!str.empty()) { | ||||||
| 			std::map<std::string, std::string> name_map; | 			expand_genblock(str + "."); | ||||||
| 			expand_genblock(std::string(), str + ".", name_map); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for (size_t i = 0; i < children.size(); i++) { | 		for (size_t i = 0; i < children.size(); i++) { | ||||||
|  | @ -1979,8 +1998,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 				buf = new AstNode(AST_GENBLOCK, buf); | 				buf = new AstNode(AST_GENBLOCK, buf); | ||||||
| 
 | 
 | ||||||
| 			if (!buf->str.empty()) { | 			if (!buf->str.empty()) { | ||||||
| 				std::map<std::string, std::string> name_map; | 				buf->expand_genblock(buf->str + "."); | ||||||
| 				buf->expand_genblock(std::string(), buf->str + ".", name_map); |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			for (size_t i = 0; i < buf->children.size(); i++) { | 			for (size_t i = 0; i < buf->children.size(); i++) { | ||||||
|  | @ -2058,8 +2076,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 			buf = selected_case->clone(); | 			buf = selected_case->clone(); | ||||||
| 
 | 
 | ||||||
| 			if (!buf->str.empty()) { | 			if (!buf->str.empty()) { | ||||||
| 				std::map<std::string, std::string> name_map; | 				buf->expand_genblock(buf->str + "."); | ||||||
| 				buf->expand_genblock(std::string(), buf->str + ".", name_map); |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			for (size_t i = 0; i < buf->children.size(); i++) { | 			for (size_t i = 0; i < buf->children.size(); i++) { | ||||||
|  | @ -3159,12 +3176,16 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 				log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str()); | 				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; | 		std::stringstream sstr; | ||||||
| 		sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; | 		sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.'; | ||||||
| 		std::string prefix = sstr.str(); | 		std::string prefix = sstr.str(); | ||||||
| 
 | 
 | ||||||
|  | 		AstNode *decl = current_scope[str]; | ||||||
|  | 		decl = decl->clone(); | ||||||
|  | 		decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion
 | ||||||
|  | 		decl->expand_genblock(prefix); | ||||||
|  | 
 | ||||||
| 		bool recommend_const_eval = false; | 		bool recommend_const_eval = false; | ||||||
| 		bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); | 		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)) | 		if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) | ||||||
|  | @ -3177,11 +3198,11 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (all_args_const) { | 			if (all_args_const) { | ||||||
| 				AstNode *func_workspace = current_scope[str]->clone(); | 				AstNode *func_workspace = decl->clone(); | ||||||
| 				func_workspace->str = NEW_ID.str(); | 				func_workspace->str = prefix_id(prefix, "$result"); | ||||||
| 				func_workspace->replace_result_wire_name_in_function(str, func_workspace->str); |  | ||||||
| 				newNode = func_workspace->eval_const_function(this); | 				newNode = func_workspace->eval_const_function(this); | ||||||
| 				delete func_workspace; | 				delete func_workspace; | ||||||
|  | 				delete decl; | ||||||
| 				goto apply_newNode; | 				goto apply_newNode; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -3192,8 +3213,6 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		size_t arg_count = 0; | 		size_t arg_count = 0; | ||||||
| 		std::map<std::string, std::string> replace_rules; |  | ||||||
| 		vector<AstNode*> added_mod_children; |  | ||||||
| 		dict<std::string, AstNode*> wire_cache; | 		dict<std::string, AstNode*> wire_cache; | ||||||
| 		vector<AstNode*> new_stmts; | 		vector<AstNode*> new_stmts; | ||||||
| 		vector<AstNode*> output_assignments; | 		vector<AstNode*> output_assignments; | ||||||
|  | @ -3203,16 +3222,17 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 			log_assert(type == AST_FCALL); | 			log_assert(type == AST_FCALL); | ||||||
| 
 | 
 | ||||||
| 			AstNode *wire = NULL; | 			AstNode *wire = NULL; | ||||||
|  | 			std::string res_name = prefix_id(prefix, "$result"); | ||||||
| 			for (auto child : decl->children) | 			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(); | 					wire = child->clone(); | ||||||
| 			log_assert(wire != NULL); | 			log_assert(wire != NULL); | ||||||
| 
 | 
 | ||||||
| 			wire->str = prefix + str; |  | ||||||
| 			wire->port_id = 0; | 			wire->port_id = 0; | ||||||
| 			wire->is_input = false; | 			wire->is_input = false; | ||||||
| 			wire->is_output = false; | 			wire->is_output = false; | ||||||
| 
 | 
 | ||||||
|  | 			current_scope[wire->str] = wire; | ||||||
| 			current_ast_mod->children.push_back(wire); | 			current_ast_mod->children.push_back(wire); | ||||||
| 			while (wire->simplify(true, false, false, 1, -1, false, false)) { } | 			while (wire->simplify(true, false, false, 1, -1, false, false)) { } | ||||||
| 
 | 
 | ||||||
|  | @ -3256,7 +3276,6 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 				if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) | 				if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) | ||||||
| 				{ | 				{ | ||||||
| 					AstNode *wire = child->clone(); | 					AstNode *wire = child->clone(); | ||||||
| 					wire->str = prefix + wire->str; |  | ||||||
| 					wire->port_id = 0; | 					wire->port_id = 0; | ||||||
| 					wire->is_input = false; | 					wire->is_input = false; | ||||||
| 					wire->is_output = false; | 					wire->is_output = false; | ||||||
|  | @ -3318,7 +3337,6 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 				else | 				else | ||||||
| 				{ | 				{ | ||||||
| 					wire = child->clone(); | 					wire = child->clone(); | ||||||
| 					wire->str = prefix + wire->str; |  | ||||||
| 					wire->port_id = 0; | 					wire->port_id = 0; | ||||||
| 					wire->is_input = false; | 					wire->is_input = false; | ||||||
| 					wire->is_output = false; | 					wire->is_output = false; | ||||||
|  | @ -3329,15 +3347,11 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 
 | 
 | ||||||
| 					wire_cache[child->str] = wire; | 					wire_cache[child->str] = wire; | ||||||
| 
 | 
 | ||||||
|  | 					current_scope[wire->str] = wire; | ||||||
| 					current_ast_mod->children.push_back(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)) { } | ||||||
| 					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()) | 				if ((child->is_input || child->is_output) && arg_count < children.size()) | ||||||
| 				{ | 				{ | ||||||
|  | @ -3381,18 +3395,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) | 		for (auto child : decl->children) | ||||||
| 			if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) | 			if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) | ||||||
| 			{ | 				new_stmts.push_back(child->clone()); | ||||||
| 				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()); | 		new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); | ||||||
| 
 | 
 | ||||||
|  | @ -3405,10 +3410,11 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	replace_fcall_with_id: | 	replace_fcall_with_id: | ||||||
|  | 		delete decl; | ||||||
| 		if (type == AST_FCALL) { | 		if (type == AST_FCALL) { | ||||||
| 			delete_children(); | 			delete_children(); | ||||||
| 			type = AST_IDENTIFIER; | 			type = AST_IDENTIFIER; | ||||||
| 			str = prefix + str; | 			str = prefix_id(prefix, "$result"); | ||||||
| 		} | 		} | ||||||
| 		if (type == AST_TCALL) | 		if (type == AST_TCALL) | ||||||
| 			str = ""; | 			str = ""; | ||||||
|  | @ -3859,63 +3865,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m | ||||||
| 	return block; | 	return block; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // annotate the names of all wires and other named objects in a generate block
 | // annotate the names of all wires and other named objects in a named generate
 | ||||||
| void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) | // 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 (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { | ||||||
| 		if (name_map.count(str) > 0) { | 		log_assert(!str.empty()); | ||||||
| 			str = name_map[str]; | 
 | ||||||
| 		} else { | 		// search starting in the innermost scope and then stepping outward
 | ||||||
| 			// remap the prefix of this ident if it is a local generate scope
 | 		for (size_t ppos = prefix.size() - 1; ppos; --ppos) { | ||||||
| 			size_t pos = str.rfind('.'); | 			if (prefix.at(ppos) != '.') continue; | ||||||
| 			if (pos != std::string::npos) { | 
 | ||||||
| 				std::string existing_prefix = str.substr(0, pos); | 			std::string new_prefix = prefix.substr(0, ppos + 1); | ||||||
| 				if (name_map.count(existing_prefix) > 0) { | 			auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { | ||||||
| 					str = name_map[existing_prefix] + str.substr(pos); | 				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; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// 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; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::map<std::string, std::string> backup_name_map; | 	auto prefix_node = [&prefix](AstNode* child) { | ||||||
| 
 | 		if (child->str.empty()) return; | ||||||
| 	auto prefix_node = [&](AstNode* child) { | 		std::string new_name = prefix_id(prefix, child->str); | ||||||
| 		if (backup_name_map.size() == 0) |  | ||||||
| 			backup_name_map = name_map; |  | ||||||
| 
 |  | ||||||
| 		// 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; |  | ||||||
| 		if (child->type == AST_FUNCTION) | 		if (child->type == AST_FUNCTION) | ||||||
| 			child->replace_result_wire_name_in_function(child->str, new_name); | 			child->replace_result_wire_name_in_function(child->str, new_name); | ||||||
| 		else | 		else | ||||||
|  | @ -3967,43 +3962,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma | ||||||
| 			continue; | 			continue; | ||||||
| 		// functions/tasks may reference wires, constants, etc. in this scope
 | 		// functions/tasks may reference wires, constants, etc. in this scope
 | ||||||
| 		if (child->type == AST_FUNCTION || child->type == AST_TASK) | 		if (child->type == AST_FUNCTION || child->type == AST_TASK) | ||||||
| 			child->expand_genblock(index_var, prefix, name_map, false); | 			continue; | ||||||
| 		// continue prefixing if this child block is anonymous
 | 		// named blocks pick up the current prefix and will expanded later
 | ||||||
| 		else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) | 		if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) | ||||||
| 			child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); | 			continue; | ||||||
| 		else | 
 | ||||||
| 			child->expand_genblock(index_var, prefix, name_map, original_scope); | 		child->expand_genblock(prefix); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	if (backup_name_map.size() > 0) |  | ||||||
| 		name_map.swap(backup_name_map); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // rename stuff (used when tasks of functions are instantiated)
 | // add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or
 | ||||||
| void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) | // IEEE 1800-2017 Section 27.6
 | ||||||
|  | void AstNode::label_genblks(std::set<std::string>& existing, int &counter) | ||||||
| { | { | ||||||
| 	if (type == AST_BLOCK) | 	switch (type) { | ||||||
| 	{ | 	case AST_GENIF: | ||||||
| 		std::map<std::string, std::string> new_rules = rules; | 	case AST_GENFOR: | ||||||
| 		std::string new_prefix = prefix + str; | 	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) | 	case AST_GENBLOCK: { | ||||||
| 			if (child->type == AST_WIRE) { | 		// if this block is unlabeled, generate its corresponding unique name
 | ||||||
| 				new_rules[child->str] = new_prefix + child->str; | 		for (int padding = 0; str.empty(); ++padding) { | ||||||
| 				child->str = new_prefix + child->str; | 			std::string candidate = "\\genblk"; | ||||||
| 			} | 			for (int i = 0; i < padding; ++i) | ||||||
| 
 | 				candidate += '0'; | ||||||
| 		for (auto child : children) | 			candidate += std::to_string(counter); | ||||||
| 			if (child->type != AST_WIRE) | 			if (!existing.count(candidate)) | ||||||
| 				child->replace_ids(new_prefix, new_rules); | 				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 | 
 | ||||||
| 	{ | 	default: | ||||||
| 		if (type == AST_IDENTIFIER && rules.count(str) > 0) | 		// track names which could conflict with implicit genblk names
 | ||||||
| 			str = rules.at(str); | 		if (str.rfind("\\genblk", 0) == 0) | ||||||
| 		for (auto child : children) | 			existing.insert(str); | ||||||
| 			child->replace_ids(prefix, rules); | 		for (AstNode *child : children) | ||||||
|  | 			child->label_genblks(existing, counter); | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -4773,6 +4780,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 
 | 
 | ||||||
| 		if (stmt->type == AST_BLOCK) | 		if (stmt->type == AST_BLOCK) | ||||||
| 		{ | 		{ | ||||||
|  | 			if (!stmt->str.empty()) | ||||||
|  | 				stmt->expand_genblock(stmt->str + "."); | ||||||
|  | 
 | ||||||
| 			block->children.erase(block->children.begin()); | 			block->children.erase(block->children.begin()); | ||||||
| 			block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); | 			block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); | ||||||
| 			stmt->children.clear(); | 			stmt->children.clear(); | ||||||
|  |  | ||||||
|  | @ -770,6 +770,7 @@ module_body: | ||||||
| 	module_body module_body_stmt | | 	module_body module_body_stmt | | ||||||
| 	/* the following line makes the generate..endgenrate keywords optional */ | 	/* the following line makes the generate..endgenrate keywords optional */ | ||||||
| 	module_body gen_stmt | | 	module_body gen_stmt | | ||||||
|  | 	module_body gen_block | | ||||||
| 	module_body ';' | | 	module_body ';' | | ||||||
| 	%empty; | 	%empty; | ||||||
| 
 | 
 | ||||||
|  | @ -2459,6 +2460,16 @@ behavioral_stmt: | ||||||
| 		exitTypeScope(); | 		exitTypeScope(); | ||||||
| 		if ($4 != NULL && $8 != NULL && *$4 != *$8) | 		if ($4 != NULL && $8 != NULL && *$4 != *$8) | ||||||
| 			frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); | 			frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); | ||||||
|  | 		AstNode *node = ast_stack.back(); | ||||||
|  | 		// In SystemVerilog, unnamed blocks with block item declarations | ||||||
|  | 		// create an implicit hierarchy scope | ||||||
|  | 		if (sv_mode && node->str.empty()) | ||||||
|  | 		    for (const AstNode* child : node->children) | ||||||
|  | 			if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER | ||||||
|  | 				|| child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { | ||||||
|  | 			    node->str = "$unnamed_block$" + std::to_string(autoidx++); | ||||||
|  | 			    break; | ||||||
|  | 			} | ||||||
| 		SET_AST_NODE_LOC(ast_stack.back(), @2, @8); | 		SET_AST_NODE_LOC(ast_stack.back(), @2, @8); | ||||||
| 		delete $4; | 		delete $4; | ||||||
| 		delete $8; | 		delete $8; | ||||||
|  | @ -2473,6 +2484,7 @@ behavioral_stmt: | ||||||
| 		ast_stack.back()->children.push_back($7); | 		ast_stack.back()->children.push_back($7); | ||||||
| 	} ';' simple_behavioral_stmt ')' { | 	} ';' simple_behavioral_stmt ')' { | ||||||
| 		AstNode *block = new AstNode(AST_BLOCK); | 		AstNode *block = new AstNode(AST_BLOCK); | ||||||
|  | 		block->str = "$for_loop$" + std::to_string(autoidx++); | ||||||
| 		ast_stack.back()->children.push_back(block); | 		ast_stack.back()->children.push_back(block); | ||||||
| 		ast_stack.push_back(block); | 		ast_stack.push_back(block); | ||||||
| 	} behavioral_stmt { | 	} behavioral_stmt { | ||||||
|  | @ -2722,6 +2734,7 @@ single_arg: | ||||||
| 
 | 
 | ||||||
| module_gen_body: | module_gen_body: | ||||||
| 	module_gen_body gen_stmt_or_module_body_stmt | | 	module_gen_body gen_stmt_or_module_body_stmt | | ||||||
|  | 	module_gen_body gen_block | | ||||||
| 	%empty; | 	%empty; | ||||||
| 
 | 
 | ||||||
| gen_stmt_or_module_body_stmt: | gen_stmt_or_module_body_stmt: | ||||||
|  | @ -2747,12 +2760,7 @@ gen_stmt: | ||||||
| 		ast_stack.back()->children.push_back(node); | 		ast_stack.back()->children.push_back(node); | ||||||
| 		ast_stack.push_back(node); | 		ast_stack.push_back(node); | ||||||
| 		ast_stack.back()->children.push_back($3); | 		ast_stack.back()->children.push_back($3); | ||||||
| 		AstNode *block = new AstNode(AST_GENBLOCK); | 	} gen_stmt_block opt_gen_else { | ||||||
| 		ast_stack.back()->children.push_back(block); |  | ||||||
| 		ast_stack.push_back(block); |  | ||||||
| 	} gen_stmt_block { |  | ||||||
| 		ast_stack.pop_back(); |  | ||||||
| 	} opt_gen_else { |  | ||||||
| 		SET_AST_NODE_LOC(ast_stack.back(), @1, @7); | 		SET_AST_NODE_LOC(ast_stack.back(), @1, @7); | ||||||
| 		ast_stack.pop_back(); | 		ast_stack.pop_back(); | ||||||
| 	} | | 	} | | ||||||
|  | @ -2765,20 +2773,6 @@ gen_stmt: | ||||||
| 		SET_AST_NODE_LOC(ast_stack.back(), @1, @7); | 		SET_AST_NODE_LOC(ast_stack.back(), @1, @7); | ||||||
| 		ast_stack.pop_back(); | 		ast_stack.pop_back(); | ||||||
| 	} | | 	} | | ||||||
| 	TOK_BEGIN { |  | ||||||
| 		enterTypeScope(); |  | ||||||
| 	} opt_label { |  | ||||||
| 		AstNode *node = new AstNode(AST_GENBLOCK); |  | ||||||
| 		node->str = $3 ? *$3 : std::string(); |  | ||||||
| 		ast_stack.back()->children.push_back(node); |  | ||||||
| 		ast_stack.push_back(node); |  | ||||||
| 	} module_gen_body TOK_END opt_label { |  | ||||||
| 		exitTypeScope(); |  | ||||||
| 		delete $3; |  | ||||||
| 		delete $7; |  | ||||||
| 		SET_AST_NODE_LOC(ast_stack.back(), @1, @7); |  | ||||||
| 		ast_stack.pop_back(); |  | ||||||
| 	} | |  | ||||||
| 	TOK_MSG_TASKS { | 	TOK_MSG_TASKS { | ||||||
| 		AstNode *node = new AstNode(AST_TECALL); | 		AstNode *node = new AstNode(AST_TECALL); | ||||||
| 		node->str = *$1; | 		node->str = *$1; | ||||||
|  | @ -2790,6 +2784,23 @@ gen_stmt: | ||||||
| 		ast_stack.pop_back(); | 		ast_stack.pop_back(); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | gen_block: | ||||||
|  | 	TOK_BEGIN { | ||||||
|  | 		enterTypeScope(); | ||||||
|  | 	} opt_label { | ||||||
|  | 		AstNode *node = new AstNode(AST_GENBLOCK); | ||||||
|  | 		node->str = $3 ? *$3 : std::string(); | ||||||
|  | 		ast_stack.back()->children.push_back(node); | ||||||
|  | 		ast_stack.push_back(node); | ||||||
|  | 	} module_gen_body TOK_END opt_label { | ||||||
|  | 		exitTypeScope(); | ||||||
|  | 		delete $3; | ||||||
|  | 		delete $7; | ||||||
|  | 		SET_AST_NODE_LOC(ast_stack.back(), @1, @7); | ||||||
|  | 		ast_stack.pop_back(); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | // result is wrapped in a genblock only if necessary | ||||||
| gen_stmt_block: | gen_stmt_block: | ||||||
| 	{ | 	{ | ||||||
| 		AstNode *node = new AstNode(AST_GENBLOCK); | 		AstNode *node = new AstNode(AST_GENBLOCK); | ||||||
|  | @ -2798,7 +2809,7 @@ gen_stmt_block: | ||||||
| 	} gen_stmt_or_module_body_stmt { | 	} gen_stmt_or_module_body_stmt { | ||||||
| 		SET_AST_NODE_LOC(ast_stack.back(), @2, @2); | 		SET_AST_NODE_LOC(ast_stack.back(), @2, @2); | ||||||
| 		ast_stack.pop_back(); | 		ast_stack.pop_back(); | ||||||
| 	}; | 	} | gen_block; | ||||||
| 
 | 
 | ||||||
| opt_gen_else: | opt_gen_else: | ||||||
| 	TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; | 	TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; | ||||||
|  |  | ||||||
|  | @ -334,6 +334,10 @@ namespace RTLIL | ||||||
| 			return compare(size()-len, len, suffix) == 0; | 			return compare(size()-len, len, suffix) == 0; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		bool contains(const char* str) const { | ||||||
|  | 			return strstr(c_str(), str); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		size_t size() const { | 		size_t size() const { | ||||||
| 			return strlen(c_str()); | 			return strlen(c_str()); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -118,19 +118,14 @@ struct TechmapWorker | ||||||
| 			return result; | 			return result; | ||||||
| 
 | 
 | ||||||
| 		for (auto w : module->wires()) { | 		for (auto w : module->wires()) { | ||||||
| 			const char *p = w->name.c_str(); | 			if (*w->name.c_str() == '$') | ||||||
| 			if (*p == '$') |  | ||||||
| 				continue; | 				continue; | ||||||
| 
 | 
 | ||||||
| 			const char *q = strrchr(p+1, '.'); | 			if (w->name.contains("_TECHMAP_") && !w->name.contains("_TECHMAP_REPLACE_")) { | ||||||
| 			if (q) |  | ||||||
| 				p = q; |  | ||||||
| 
 |  | ||||||
| 			if (!strncmp(p, "\\_TECHMAP_", 10)) { |  | ||||||
| 				TechmapWireData record; | 				TechmapWireData record; | ||||||
| 				record.wire = w; | 				record.wire = w; | ||||||
| 				record.value = w; | 				record.value = w; | ||||||
| 				result[p].push_back(record); | 				result[w->name].push_back(record); | ||||||
| 				w->set_bool_attribute(ID::keep); | 				w->set_bool_attribute(ID::keep); | ||||||
| 				w->set_bool_attribute(ID::_techmap_special_); | 				w->set_bool_attribute(ID::_techmap_special_); | ||||||
| 			} | 			} | ||||||
|  | @ -165,7 +160,7 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 		orig_cell_name = cell->name.str(); | 		orig_cell_name = cell->name.str(); | ||||||
| 		for (auto tpl_cell : tpl->cells()) | 		for (auto tpl_cell : tpl->cells()) | ||||||
| 			if (tpl_cell->name == ID::_TECHMAP_REPLACE_) { | 			if (tpl_cell->name.ends_with("_TECHMAP_REPLACE_")) { | ||||||
| 				module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); | 				module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
|  | @ -226,8 +221,8 @@ struct TechmapWorker | ||||||
| 			} | 			} | ||||||
| 			design->select(module, w); | 			design->select(module, w); | ||||||
| 
 | 
 | ||||||
| 			if (tpl_w->name.begins_with("\\_TECHMAP_REPLACE_.")) { | 			if (const char *p = strstr(tpl_w->name.c_str(), "_TECHMAP_REPLACE_.")) { | ||||||
| 				IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), tpl_w->name.c_str() + strlen("\\_TECHMAP_REPLACE_")); | 				IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_")); | ||||||
| 				Wire *replace_w = module->addWire(replace_name, tpl_w); | 				Wire *replace_w = module->addWire(replace_name, tpl_w); | ||||||
| 				module->connect(replace_w, w); | 				module->connect(replace_w, w); | ||||||
| 			} | 			} | ||||||
|  | @ -327,12 +322,12 @@ struct TechmapWorker | ||||||
| 		for (auto tpl_cell : tpl->cells()) | 		for (auto tpl_cell : tpl->cells()) | ||||||
| 		{ | 		{ | ||||||
| 			IdString c_name = tpl_cell->name; | 			IdString c_name = tpl_cell->name; | ||||||
| 			bool techmap_replace_cell = (c_name == ID::_TECHMAP_REPLACE_); | 			bool techmap_replace_cell = c_name.ends_with("_TECHMAP_REPLACE_"); | ||||||
| 
 | 
 | ||||||
| 			if (techmap_replace_cell) | 			if (techmap_replace_cell) | ||||||
| 				c_name = orig_cell_name; | 				c_name = orig_cell_name; | ||||||
| 			else if (tpl_cell->name.begins_with("\\_TECHMAP_REPLACE_.")) | 			else if (const char *p = strstr(tpl_cell->name.c_str(), "_TECHMAP_REPLACE_.")) | ||||||
| 				c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_")); | 				c_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_")); | ||||||
| 			else | 			else | ||||||
| 				apply_prefix(cell->name, c_name); | 				apply_prefix(cell->name, c_name); | ||||||
| 
 | 
 | ||||||
|  | @ -730,12 +725,16 @@ struct TechmapWorker | ||||||
| 						for (auto &it : twd) | 						for (auto &it : twd) | ||||||
| 							techmap_wire_names.insert(it.first); | 							techmap_wire_names.insert(it.first); | ||||||
| 
 | 
 | ||||||
| 						for (auto &it : twd[ID::_TECHMAP_FAIL_]) { | 						for (auto &it : twd) { | ||||||
| 							RTLIL::SigSpec value = it.value; | 							if (!it.first.ends_with("_TECHMAP_FAIL_")) | ||||||
| 							if (value.is_fully_const() && value.as_bool()) { | 								continue; | ||||||
| 								log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", | 							for (const TechmapWireData &elem : it.second) { | ||||||
| 										derived_name.c_str(), log_id(it.wire->name), log_signal(value)); | 								RTLIL::SigSpec value = elem.value; | ||||||
| 								techmap_do_cache[tpl] = false; | 								if (value.is_fully_const() && value.as_bool()) { | ||||||
|  | 									log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", | ||||||
|  | 											derived_name.c_str(), log_id(elem.wire->name), log_signal(value)); | ||||||
|  | 									techmap_do_cache[tpl] = false; | ||||||
|  | 								} | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
|  | @ -744,7 +743,7 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 						for (auto &it : twd) | 						for (auto &it : twd) | ||||||
| 						{ | 						{ | ||||||
| 							if (!it.first.begins_with("\\_TECHMAP_DO_") || it.second.empty()) | 							if (!it.first.contains("_TECHMAP_DO_") || it.second.empty()) | ||||||
| 								continue; | 								continue; | ||||||
| 
 | 
 | ||||||
| 							auto &data = it.second.front(); | 							auto &data = it.second.front(); | ||||||
|  | @ -756,7 +755,7 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 							const char *p = data.wire->name.c_str(); | 							const char *p = data.wire->name.c_str(); | ||||||
| 							const char *q = strrchr(p+1, '.'); | 							const char *q = strrchr(p+1, '.'); | ||||||
| 							q = q ? q : p+1; | 							q = q ? q+1 : p+1; | ||||||
| 
 | 
 | ||||||
| 							std::string cmd_string = data.value.as_const().decode_string(); | 							std::string cmd_string = data.value.as_const().decode_string(); | ||||||
| 
 | 
 | ||||||
|  | @ -873,7 +872,7 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 					TechmapWires twd = techmap_find_special_wires(tpl); | 					TechmapWires twd = techmap_find_special_wires(tpl); | ||||||
| 					for (auto &it : twd) { | 					for (auto &it : twd) { | ||||||
| 						if (it.first != ID::_TECHMAP_FAIL_ && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.begins_with("\\_TECHMAP_DO_") && !it.first.begins_with("\\_TECHMAP_DONE_")) | 						if (!it.first.ends_with("_TECHMAP_FAIL_") && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.contains("_TECHMAP_DO_") && !it.first.contains("_TECHMAP_DONE_")) | ||||||
| 							log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first)); | 							log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first)); | ||||||
| 						if (techmap_do_cache[tpl]) | 						if (techmap_do_cache[tpl]) | ||||||
| 							for (auto &it2 : it.second) | 							for (auto &it2 : it.second) | ||||||
|  |  | ||||||
|  | @ -41,10 +41,7 @@ generate | ||||||
|         wire [WIDTH-1:0] BB = {{(WIDTH-B_WIDTH){B_SIGNED ? B[B_WIDTH-1] : 1'b0}}, B}; |         wire [WIDTH-1:0] BB = {{(WIDTH-B_WIDTH){B_SIGNED ? B[B_WIDTH-1] : 1'b0}}, B}; | ||||||
|         // For $ge operation, start with the assumption that A and B are
 |         // For $ge operation, start with the assumption that A and B are
 | ||||||
|         //   equal (propagating this equality if A and B turn out to be so)
 |         //   equal (propagating this equality if A and B turn out to be so)
 | ||||||
|         if (_TECHMAP_CELLTYPE_ == "$ge") |         localparam CI = _TECHMAP_CELLTYPE_ == "$ge"; | ||||||
|             localparam CI = 1'b1; |  | ||||||
|         else |  | ||||||
|             localparam CI = 1'b0; |  | ||||||
|         $__CMP2LCU #(.AB_WIDTH(WIDTH), .AB_SIGNED(A_SIGNED && B_SIGNED), .LCU_WIDTH(1), .BUDGET(`LUT_WIDTH), .CI(CI)) |         $__CMP2LCU #(.AB_WIDTH(WIDTH), .AB_SIGNED(A_SIGNED && B_SIGNED), .LCU_WIDTH(1), .BUDGET(`LUT_WIDTH), .CI(CI)) | ||||||
|             _TECHMAP_REPLACE_ (.A(AA), .B(BB), .P(1'b1), .G(1'b0), .Y(Y)); |             _TECHMAP_REPLACE_ (.A(AA), .B(BB), .P(1'b1), .G(1'b0), .Y(Y)); | ||||||
|     end |     end | ||||||
|  | @ -81,12 +78,12 @@ generate | ||||||
|         assign Y = CO[LCU_WIDTH-1]; |         assign Y = CO[LCU_WIDTH-1]; | ||||||
|     end |     end | ||||||
|     else begin |     else begin | ||||||
|         if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]) |         localparam COST = | ||||||
|             localparam COST = 0; |             _TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0] | ||||||
|         else if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]) |             ? 0 | ||||||
|             localparam COST = 1; |             : (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0] | ||||||
|         else |                 ? 1 | ||||||
|             localparam COST = 2; |                 : 2); | ||||||
| 
 | 
 | ||||||
|         if (BUDGET < COST) |         if (BUDGET < COST) | ||||||
|              $__CMP2LCU #(.AB_WIDTH(AB_WIDTH), .AB_SIGNED(AB_SIGNED), .LCU_WIDTH(LCU_WIDTH+1), .BUDGET(`LUT_WIDTH), .CI(CI)) |              $__CMP2LCU #(.AB_WIDTH(AB_WIDTH), .AB_SIGNED(AB_SIGNED), .LCU_WIDTH(LCU_WIDTH+1), .BUDGET(`LUT_WIDTH), .CI(CI)) | ||||||
|  | @ -104,21 +101,21 @@ generate | ||||||
|                 //   from MSB down, deferring to less significant bits if the
 |                 //   from MSB down, deferring to less significant bits if the
 | ||||||
|                 //   MSBs are equal
 |                 //   MSBs are equal
 | ||||||
|                 assign GG = P[0] & (A[AB_WIDTH-1] & ~B[AB_WIDTH-1]); |                 assign GG = P[0] & (A[AB_WIDTH-1] & ~B[AB_WIDTH-1]); | ||||||
|  |             (* force_downto *) | ||||||
|  |             wire [LCU_WIDTH-1:0] P_, G_; | ||||||
|             if (LCU_WIDTH == 1) begin |             if (LCU_WIDTH == 1) begin | ||||||
|                 // Propagate only if all pairs are equal
 |                 // Propagate only if all pairs are equal
 | ||||||
|                 //   (inconclusive evidence to say A >= B)
 |                 //   (inconclusive evidence to say A >= B)
 | ||||||
|                 wire P_ = P[0] & PP; |                 assign P_ = P[0] & PP; | ||||||
|                 // Generate if any comparisons call for it
 |                 // Generate if any comparisons call for it
 | ||||||
|                 wire G_ = G[0] | GG; |                 assign G_ = G[0] | GG; | ||||||
|             end |             end | ||||||
|             else begin |             else begin | ||||||
|                 // Propagate only if all pairs are equal
 |                 // Propagate only if all pairs are equal
 | ||||||
|                 //   (inconclusive evidence to say A >= B)
 |                 //   (inconclusive evidence to say A >= B)
 | ||||||
|                 (* force_downto *) |                 assign P_ = {P[LCU_WIDTH-1:1], P[0] & PP}; | ||||||
|                 wire [LCU_WIDTH-1:0] P_ = {P[LCU_WIDTH-1:1], P[0] & PP}; |  | ||||||
|                 // Generate if any comparisons call for it
 |                 // Generate if any comparisons call for it
 | ||||||
|                 (* force_downto *) |                 assign G_ = {G[LCU_WIDTH-1:1], G[0] | GG}; | ||||||
|                 wire [LCU_WIDTH-1:0] G_ = {G[LCU_WIDTH-1:1], G[0] | GG}; |  | ||||||
|             end |             end | ||||||
|             if (AB_WIDTH == 1) |             if (AB_WIDTH == 1) | ||||||
|                $__CMP2LCU #(.AB_WIDTH(AB_WIDTH-1), .AB_SIGNED(1'b0), .LCU_WIDTH(LCU_WIDTH), .BUDGET(BUDGET-COST), .CI(CI)) |                $__CMP2LCU #(.AB_WIDTH(AB_WIDTH-1), .AB_SIGNED(1'b0), .LCU_WIDTH(LCU_WIDTH), .BUDGET(BUDGET-COST), .CI(CI)) | ||||||
|  |  | ||||||
|  | @ -66,14 +66,12 @@ function automatic [(1 << `LUT_WIDTH)-1:0] gen_lut; | ||||||
| endfunction | endfunction | ||||||
| 
 | 
 | ||||||
| generate | generate | ||||||
| 	if (_TECHMAP_CELLTYPE_ == "$lt") | 	localparam operation = | ||||||
| 		localparam operation = 0; | 		_TECHMAP_CELLTYPE_ == "$lt" ? 0 : | ||||||
| 	if (_TECHMAP_CELLTYPE_ == "$le") | 		_TECHMAP_CELLTYPE_ == "$le" ? 1 : | ||||||
| 		localparam operation = 1; | 		_TECHMAP_CELLTYPE_ == "$gt" ? 2 : | ||||||
| 	if (_TECHMAP_CELLTYPE_ == "$gt") | 		_TECHMAP_CELLTYPE_ == "$ge" ? 3 : | ||||||
| 		localparam operation = 2; | 		-1; | ||||||
| 	if (_TECHMAP_CELLTYPE_ == "$ge") |  | ||||||
| 		localparam operation = 3; |  | ||||||
| 
 | 
 | ||||||
| 	if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1) | 	if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1) | ||||||
| 		wire _TECHMAP_FAIL_ = 1; | 		wire _TECHMAP_FAIL_ = 1; | ||||||
|  |  | ||||||
|  | @ -121,7 +121,7 @@ module _80_mul (A, B, Y); | ||||||
| 			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL); | 			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL); | ||||||
| 			localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom); | 			localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
| 			localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH; | 			localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH; | ||||||
| 			if (A_SIGNED && B_SIGNED) begin | 			if (A_SIGNED && B_SIGNED) begin : blk | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
| 				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; | 				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
|  | @ -129,7 +129,7 @@ module _80_mul (A, B, Y); | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
| 				wire signed [Y_WIDTH-1:0] partial_sum [n:0]; | 				wire signed [Y_WIDTH-1:0] partial_sum [n:0]; | ||||||
| 			end | 			end | ||||||
| 			else begin | 			else begin : blk | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
| 				wire [partial_Y_WIDTH-1:0] partial [n-1:0]; | 				wire [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
|  | @ -148,15 +148,15 @@ module _80_mul (A, B, Y); | ||||||
| 				) mul ( | 				) mul ( | ||||||
| 					.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}), | 					.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}), | ||||||
| 					.B(B), | 					.B(B), | ||||||
| 					.Y(partial[i]) | 					.Y(blk.partial[i]) | ||||||
| 				); | 				); | ||||||
| 				// TODO: Currently a 'cascade' approach to summing the partial
 | 				// TODO: Currently a 'cascade' approach to summing the partial
 | ||||||
| 				//       products is taken here, but a more efficient 'binary
 | 				//       products is taken here, but a more efficient 'binary
 | ||||||
| 				//       reduction' approach also exists...
 | 				//       reduction' approach also exists...
 | ||||||
| 				if (i == 0) | 				if (i == 0) | ||||||
| 					assign partial_sum[i] = partial[i]; | 					assign blk.partial_sum[i] = blk.partial[i]; | ||||||
| 				else | 				else | ||||||
| 					assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1]; | 					assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1]; | ||||||
| 			end | 			end | ||||||
| 
 | 
 | ||||||
| 			\$__mul #( | 			\$__mul #( | ||||||
|  | @ -168,17 +168,17 @@ module _80_mul (A, B, Y); | ||||||
| 			) sliceA.last ( | 			) sliceA.last ( | ||||||
| 				.A(A[A_WIDTH-1 -: last_A_WIDTH]), | 				.A(A[A_WIDTH-1 -: last_A_WIDTH]), | ||||||
| 				.B(B), | 				.B(B), | ||||||
| 				.Y(last_partial) | 				.Y(blk.last_partial) | ||||||
| 			); | 			); | ||||||
| 			assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1]; | 			assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1]; | ||||||
| 			assign Y = partial_sum[n]; | 			assign Y = blk.partial_sum[n]; | ||||||
| 		end | 		end | ||||||
| 		else if (B_WIDTH > `DSP_B_MAXWIDTH) begin | 		else if (B_WIDTH > `DSP_B_MAXWIDTH) begin | ||||||
| 			localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom); | 			localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
| 			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL); | 			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL); | ||||||
| 			localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom); | 			localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
| 			localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH; | 			localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH; | ||||||
| 			if (A_SIGNED && B_SIGNED) begin | 			if (A_SIGNED && B_SIGNED) begin : blk | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
| 				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; | 				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
|  | @ -186,7 +186,7 @@ module _80_mul (A, B, Y); | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
| 				wire signed [Y_WIDTH-1:0] partial_sum [n:0]; | 				wire signed [Y_WIDTH-1:0] partial_sum [n:0]; | ||||||
| 			end | 			end | ||||||
| 			else begin | 			else begin : blk | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
| 				wire [partial_Y_WIDTH-1:0] partial [n-1:0]; | 				wire [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
| 				(* force_downto *) | 				(* force_downto *) | ||||||
|  | @ -205,15 +205,15 @@ module _80_mul (A, B, Y); | ||||||
| 				) mul ( | 				) mul ( | ||||||
| 					.A(A), | 					.A(A), | ||||||
| 					.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}), | 					.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}), | ||||||
| 					.Y(partial[i]) | 					.Y(blk.partial[i]) | ||||||
| 				); | 				); | ||||||
| 				// TODO: Currently a 'cascade' approach to summing the partial
 | 				// TODO: Currently a 'cascade' approach to summing the partial
 | ||||||
| 				//       products is taken here, but a more efficient 'binary
 | 				//       products is taken here, but a more efficient 'binary
 | ||||||
| 				//       reduction' approach also exists...
 | 				//       reduction' approach also exists...
 | ||||||
| 				if (i == 0) | 				if (i == 0) | ||||||
| 					assign partial_sum[i] = partial[i]; | 					assign blk.partial_sum[i] = blk.partial[i]; | ||||||
| 				else | 				else | ||||||
| 					assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1]; | 					assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1]; | ||||||
| 			end | 			end | ||||||
| 
 | 
 | ||||||
| 			\$__mul #( | 			\$__mul #( | ||||||
|  | @ -225,20 +225,24 @@ module _80_mul (A, B, Y); | ||||||
| 			) mul_sliceB_last ( | 			) mul_sliceB_last ( | ||||||
| 				.A(A), | 				.A(A), | ||||||
| 				.B(B[B_WIDTH-1 -: last_B_WIDTH]), | 				.B(B[B_WIDTH-1 -: last_B_WIDTH]), | ||||||
| 				.Y(last_partial) | 				.Y(blk.last_partial) | ||||||
| 			); | 			); | ||||||
| 			assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1]; | 			assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1]; | ||||||
| 			assign Y = partial_sum[n]; | 			assign Y = blk.partial_sum[n]; | ||||||
| 		end | 		end | ||||||
| 		else begin | 		else begin | ||||||
| 			if (A_SIGNED) | 			if (A_SIGNED) begin : blkA | ||||||
| 				wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A); | 				wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A); | ||||||
| 			else | 			end | ||||||
|  | 			else begin : blkA | ||||||
| 				wire [`DSP_A_MAXWIDTH-1:0] Aext = A; | 				wire [`DSP_A_MAXWIDTH-1:0] Aext = A; | ||||||
| 			if (B_SIGNED) | 			end | ||||||
|  | 			if (B_SIGNED) begin : blkB | ||||||
| 				wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B); | 				wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B); | ||||||
| 			else | 			end | ||||||
|  | 			else begin : blkB | ||||||
| 				wire [`DSP_B_MAXWIDTH-1:0] Bext = B; | 				wire [`DSP_B_MAXWIDTH-1:0] Bext = B; | ||||||
|  | 			end | ||||||
| 
 | 
 | ||||||
| 			`DSP_NAME #( | 			`DSP_NAME #( | ||||||
| 				.A_SIGNED(A_SIGNED), | 				.A_SIGNED(A_SIGNED), | ||||||
|  | @ -247,8 +251,8 @@ module _80_mul (A, B, Y); | ||||||
| 				.B_WIDTH(`DSP_B_MAXWIDTH), | 				.B_WIDTH(`DSP_B_MAXWIDTH), | ||||||
| 				.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)), | 				.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)), | ||||||
| 			) _TECHMAP_REPLACE_ ( | 			) _TECHMAP_REPLACE_ ( | ||||||
| 				.A(Aext), | 				.A(blkA.Aext), | ||||||
| 				.B(Bext), | 				.B(blkB.Bext), | ||||||
| 				.Y(Y) | 				.Y(Y) | ||||||
| 			); | 			); | ||||||
| 		end | 		end | ||||||
|  |  | ||||||
|  | @ -254,6 +254,41 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B | ||||||
| 
 | 
 | ||||||
| 	wire [15:0] A1DATA_16, B1DATA_16; | 	wire [15:0] A1DATA_16, B1DATA_16; | ||||||
| 
 | 
 | ||||||
|  | `define INSTANCE \ | ||||||
|  | 	\$__ICE40_RAM4K #( \ | ||||||
|  | 		.READ_MODE(MODE), \ | ||||||
|  | 		.WRITE_MODE(MODE), \ | ||||||
|  | 		.NEGCLK_R(!CLKPOL2), \ | ||||||
|  | 		.NEGCLK_W(!CLKPOL3), \ | ||||||
|  | 		.INIT_0(INIT_0), \ | ||||||
|  | 		.INIT_1(INIT_1), \ | ||||||
|  | 		.INIT_2(INIT_2), \ | ||||||
|  | 		.INIT_3(INIT_3), \ | ||||||
|  | 		.INIT_4(INIT_4), \ | ||||||
|  | 		.INIT_5(INIT_5), \ | ||||||
|  | 		.INIT_6(INIT_6), \ | ||||||
|  | 		.INIT_7(INIT_7), \ | ||||||
|  | 		.INIT_8(INIT_8), \ | ||||||
|  | 		.INIT_9(INIT_9), \ | ||||||
|  | 		.INIT_A(INIT_A), \ | ||||||
|  | 		.INIT_B(INIT_B), \ | ||||||
|  | 		.INIT_C(INIT_C), \ | ||||||
|  | 		.INIT_D(INIT_D), \ | ||||||
|  | 		.INIT_E(INIT_E), \ | ||||||
|  | 		.INIT_F(INIT_F) \ | ||||||
|  | 	) _TECHMAP_REPLACE_ ( \ | ||||||
|  | 		.RDATA(A1DATA_16), \ | ||||||
|  | 		.RADDR(A1ADDR_11), \ | ||||||
|  | 		.RCLK(CLK2), \ | ||||||
|  | 		.RCLKE(A1EN), \ | ||||||
|  | 		.RE(1'b1), \ | ||||||
|  | 		.WDATA(B1DATA_16), \ | ||||||
|  | 		.WADDR(B1ADDR_11), \ | ||||||
|  | 		.WCLK(CLK3), \ | ||||||
|  | 		.WCLKE(|B1EN), \ | ||||||
|  | 		.WE(1'b1) \ | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
| 	generate | 	generate | ||||||
| 		if (MODE == 1) begin | 		if (MODE == 1) begin | ||||||
| 			assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8], | 			assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8], | ||||||
|  | @ -261,51 +296,23 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B | ||||||
| 			assign {B1DATA_16[14], B1DATA_16[12], B1DATA_16[10], B1DATA_16[ 8], | 			assign {B1DATA_16[14], B1DATA_16[12], B1DATA_16[10], B1DATA_16[ 8], | ||||||
| 			        B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA; | 			        B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA; | ||||||
| 			`include "brams_init1.vh" | 			`include "brams_init1.vh" | ||||||
|  | 			`INSTANCE | ||||||
| 		end | 		end | ||||||
| 		if (MODE == 2) begin | 		if (MODE == 2) begin | ||||||
| 			assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]}; | 			assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]}; | ||||||
| 			assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA; | 			assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA; | ||||||
| 			`include "brams_init2.vh" | 			`include "brams_init2.vh" | ||||||
|  | 			`INSTANCE | ||||||
| 		end | 		end | ||||||
| 		if (MODE == 3) begin | 		if (MODE == 3) begin | ||||||
| 			assign A1DATA = {A1DATA_16[11], A1DATA_16[3]}; | 			assign A1DATA = {A1DATA_16[11], A1DATA_16[3]}; | ||||||
| 			assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA; | 			assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA; | ||||||
| 			`include "brams_init3.vh" | 			`include "brams_init3.vh" | ||||||
|  | 			`INSTANCE | ||||||
| 		end | 		end | ||||||
| 	endgenerate | 	endgenerate | ||||||
| 
 | 
 | ||||||
| 	\$__ICE40_RAM4K #( | `undef INSTANCE | ||||||
| 		.READ_MODE(MODE), | 
 | ||||||
| 		.WRITE_MODE(MODE), |  | ||||||
| 		.NEGCLK_R(!CLKPOL2), |  | ||||||
| 		.NEGCLK_W(!CLKPOL3), |  | ||||||
| 		.INIT_0(INIT_0), |  | ||||||
| 		.INIT_1(INIT_1), |  | ||||||
| 		.INIT_2(INIT_2), |  | ||||||
| 		.INIT_3(INIT_3), |  | ||||||
| 		.INIT_4(INIT_4), |  | ||||||
| 		.INIT_5(INIT_5), |  | ||||||
| 		.INIT_6(INIT_6), |  | ||||||
| 		.INIT_7(INIT_7), |  | ||||||
| 		.INIT_8(INIT_8), |  | ||||||
| 		.INIT_9(INIT_9), |  | ||||||
| 		.INIT_A(INIT_A), |  | ||||||
| 		.INIT_B(INIT_B), |  | ||||||
| 		.INIT_C(INIT_C), |  | ||||||
| 		.INIT_D(INIT_D), |  | ||||||
| 		.INIT_E(INIT_E), |  | ||||||
| 		.INIT_F(INIT_F) |  | ||||||
| 	) _TECHMAP_REPLACE_ ( |  | ||||||
| 		.RDATA(A1DATA_16), |  | ||||||
| 		.RADDR(A1ADDR_11), |  | ||||||
| 		.RCLK(CLK2), |  | ||||||
| 		.RCLKE(A1EN), |  | ||||||
| 		.RE(1'b1), |  | ||||||
| 		.WDATA(B1DATA_16), |  | ||||||
| 		.WADDR(B1ADDR_11), |  | ||||||
| 		.WCLK(CLK3), |  | ||||||
| 		.WCLKE(|B1EN), |  | ||||||
| 		.WE(1'b1) |  | ||||||
| 	); |  | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -151,6 +151,8 @@ generate if (`LUT_SIZE == 4) begin | ||||||
| 		); | 		); | ||||||
| 	end endgenerate | 	end endgenerate | ||||||
| 
 | 
 | ||||||
|  | 	assign X = S; | ||||||
|  | 
 | ||||||
| end else begin | end else begin | ||||||
| 
 | 
 | ||||||
| 	localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; | 	localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; | ||||||
|  | @ -193,8 +195,8 @@ end else begin | ||||||
| 		end | 		end | ||||||
| 	end endgenerate | 	end endgenerate | ||||||
| 
 | 
 | ||||||
| end endgenerate |  | ||||||
| 
 |  | ||||||
| 	assign X = S; | 	assign X = S; | ||||||
|  | 
 | ||||||
|  | end endgenerate | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								tests/simple/func_block.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tests/simple/func_block.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | `default_nettype none | ||||||
|  | 
 | ||||||
|  | module top(inp, out1, out2, out3); | ||||||
|  | 	input wire [31:0] inp; | ||||||
|  | 
 | ||||||
|  | 	function automatic [31:0] func1; | ||||||
|  | 		input [31:0] inp; | ||||||
|  | 		reg [31:0] idx; | ||||||
|  | 		for (idx = 0; idx < 32; idx = idx + 1) begin : blk | ||||||
|  | 			func1[idx] = (idx & 1'b1) ^ inp[idx]; | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	function automatic [31:0] func2; | ||||||
|  | 		input [31:0] inp; | ||||||
|  | 		reg [31:0] idx; | ||||||
|  | 		for (idx = 0; idx < 32; idx = idx + 1) begin : blk | ||||||
|  | 			func2[idx] = (idx & 1'b1) ^ inp[idx]; | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	function automatic [31:0] func3; | ||||||
|  | 		localparam A = 32 - 1; | ||||||
|  | 		parameter B = 1 - 0; | ||||||
|  | 		input [31:0] inp; | ||||||
|  | 		func3[A:B] = inp[A:B]; | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	output wire [31:0] out1, out2, out3; | ||||||
|  | 	assign out1 = func1(inp); | ||||||
|  | 	assign out2 = func2(inp); | ||||||
|  | 	assign out3 = func3(inp); | ||||||
|  | endmodule | ||||||
							
								
								
									
										25
									
								
								tests/simple/func_recurse.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tests/simple/func_recurse.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | module top( | ||||||
|  | 	input wire [3:0] inp, | ||||||
|  | 	output wire [3:0] out1, out2 | ||||||
|  | ); | ||||||
|  | 	function automatic [3:0] pow_a; | ||||||
|  | 		input [3:0] base, exp; | ||||||
|  | 		begin | ||||||
|  | 			pow_a = 1; | ||||||
|  | 			if (exp > 0) | ||||||
|  | 				pow_a = base * pow_a(base, exp - 1); | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	function automatic [3:0] pow_b; | ||||||
|  | 		input [3:0] base, exp; | ||||||
|  | 		begin | ||||||
|  | 			pow_b = 1; | ||||||
|  | 			if (exp > 0) | ||||||
|  | 				pow_b = base * pow_b(base, exp - 1); | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	assign out1 = pow_a(inp, 3); | ||||||
|  | 	assign out2 = pow_b(2, 2); | ||||||
|  | endmodule | ||||||
							
								
								
									
										41
									
								
								tests/simple/func_width_scope.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/simple/func_width_scope.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | module top(inp, out1, out2); | ||||||
|  | 	input wire signed inp; | ||||||
|  | 
 | ||||||
|  | 	localparam WIDTH_A = 5; | ||||||
|  | 	function automatic [WIDTH_A-1:0] func1; | ||||||
|  | 		input reg [WIDTH_A-1:0] inp; | ||||||
|  | 		func1 = ~inp; | ||||||
|  | 	endfunction | ||||||
|  | 	wire [func1(0)-1:0] xc; | ||||||
|  | 	assign xc = 1'sb1; | ||||||
|  | 	wire [WIDTH_A-1:0] xn; | ||||||
|  | 	assign xn = func1(inp); | ||||||
|  | 
 | ||||||
|  | 	generate | ||||||
|  | 		if (1) begin : blk | ||||||
|  | 			localparam WIDTH_A = 6; | ||||||
|  | 			function automatic [WIDTH_A-1:0] func2; | ||||||
|  | 				input reg [WIDTH_A-1:0] inp; | ||||||
|  | 				func2 = ~inp; | ||||||
|  | 			endfunction | ||||||
|  | 			wire [func2(0)-1:0] yc; | ||||||
|  | 			assign yc = 1'sb1; | ||||||
|  | 			wire [WIDTH_A-1:0] yn; | ||||||
|  | 			assign yn = func2(inp); | ||||||
|  | 
 | ||||||
|  | 			localparam WIDTH_B = 7; | ||||||
|  | 			function automatic [WIDTH_B-1:0] func3; | ||||||
|  | 				input reg [WIDTH_B-1:0] inp; | ||||||
|  | 				func3 = ~inp; | ||||||
|  | 			endfunction | ||||||
|  | 			wire [func3(0)-1:0] zc; | ||||||
|  | 			assign zc = 1'sb1; | ||||||
|  | 			wire [WIDTH_B-1:0] zn; | ||||||
|  | 			assign zn = func3(inp); | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | 
 | ||||||
|  | 	output wire [1023:0] out1, out2; | ||||||
|  | 	assign out1 = {xc, 1'b0, blk.yc, 1'b0, blk.zc}; | ||||||
|  | 	assign out2 = {xn, 1'b0, blk.yn, 1'b0, blk.zn}; | ||||||
|  | endmodule | ||||||
							
								
								
									
										27
									
								
								tests/simple/genblk_collide.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tests/simple/genblk_collide.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | `default_nettype none | ||||||
|  | 
 | ||||||
|  | module top1; | ||||||
|  | 	generate | ||||||
|  | 		if (1) begin : foo | ||||||
|  | 			if (1) begin : bar | ||||||
|  | 				wire x; | ||||||
|  | 			end | ||||||
|  | 			assign bar.x = 1; | ||||||
|  | 			wire y; | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module top2; | ||||||
|  | 	genvar i; | ||||||
|  | 	generate | ||||||
|  | 		if (1) begin : foo | ||||||
|  | 			wire x; | ||||||
|  | 			for (i = 0; i < 1; i = i + 1) begin : foo | ||||||
|  | 				if (1) begin : foo | ||||||
|  | 					assign x = 1; | ||||||
|  | 				end | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
							
								
								
									
										21
									
								
								tests/simple/genblk_dive.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/simple/genblk_dive.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | `default_nettype none | ||||||
|  | module top(output wire x); | ||||||
|  | 	generate | ||||||
|  | 		if (1) begin : Z | ||||||
|  | 			if (1) begin : A | ||||||
|  | 				wire x; | ||||||
|  | 				if (1) begin : B | ||||||
|  | 					wire x; | ||||||
|  | 					if (1) begin : C | ||||||
|  | 						wire x; | ||||||
|  | 						assign B.x = 0; | ||||||
|  | 						wire z = A.B.C.x; | ||||||
|  | 					end | ||||||
|  | 					assign A.x = A.B.C.x; | ||||||
|  | 				end | ||||||
|  | 				assign B.C.x = B.x; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | 	assign x = Z.A.x; | ||||||
|  | endmodule | ||||||
							
								
								
									
										18
									
								
								tests/simple/genblk_order.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tests/simple/genblk_order.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | `default_nettype none | ||||||
|  | module top( | ||||||
|  | 	output wire out1, | ||||||
|  | 	output wire out2 | ||||||
|  | ); | ||||||
|  | 	generate | ||||||
|  | 		if (1) begin : outer | ||||||
|  | 			if (1) begin : foo | ||||||
|  | 				wire x = 0; | ||||||
|  | 				if (1) begin : foo | ||||||
|  | 					wire x = 1; | ||||||
|  | 					assign out1 = foo.x; | ||||||
|  | 				end | ||||||
|  | 				assign out2 = foo.x; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
|  | @ -260,3 +260,66 @@ module gen_test8; | ||||||
| 	`ASSERT(gen_test8.A.C.x == 1) | 	`ASSERT(gen_test8.A.C.x == 1) | ||||||
| 	`ASSERT(gen_test8.A.B.x == 0) | 	`ASSERT(gen_test8.A.B.x == 0) | ||||||
| endmodule | endmodule | ||||||
|  | 
 | ||||||
|  | // ------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | module gen_test9; | ||||||
|  | 
 | ||||||
|  | // `define VERIFY
 | ||||||
|  | `ifdef VERIFY | ||||||
|  | 	`define ASSERT(expr) assert property (expr); | ||||||
|  | `else | ||||||
|  | 	`define ASSERT(expr) | ||||||
|  | `endif | ||||||
|  | 
 | ||||||
|  | 	wire [1:0] w = 2'b11; | ||||||
|  | 	generate | ||||||
|  | 		begin : A | ||||||
|  | 			wire [1:0] x; | ||||||
|  | 			begin : B | ||||||
|  | 				wire [1:0] y = 2'b00; | ||||||
|  | 				`ASSERT(w == 3) | ||||||
|  | 				`ASSERT(x == 2) | ||||||
|  | 				`ASSERT(y == 0) | ||||||
|  | 				`ASSERT(A.x == 2) | ||||||
|  | 				`ASSERT(A.C.z == 1) | ||||||
|  | 				`ASSERT(A.B.y == 0) | ||||||
|  | 				`ASSERT(gen_test9.w == 3) | ||||||
|  | 				`ASSERT(gen_test9.A.x == 2) | ||||||
|  | 				`ASSERT(gen_test9.A.C.z == 1) | ||||||
|  | 				`ASSERT(gen_test9.A.B.y == 0) | ||||||
|  | 			end | ||||||
|  | 			begin : C | ||||||
|  | 				wire [1:0] z = 2'b01; | ||||||
|  | 				`ASSERT(w == 3) | ||||||
|  | 				`ASSERT(x == 2) | ||||||
|  | 				`ASSERT(z == 1) | ||||||
|  | 				`ASSERT(A.x == 2) | ||||||
|  | 				`ASSERT(A.C.z == 1) | ||||||
|  | 				`ASSERT(A.B.y == 0) | ||||||
|  | 				`ASSERT(gen_test9.w == 3) | ||||||
|  | 				`ASSERT(gen_test9.A.x == 2) | ||||||
|  | 				`ASSERT(gen_test9.A.C.z == 1) | ||||||
|  | 				`ASSERT(gen_test9.A.B.y == 0) | ||||||
|  | 			end | ||||||
|  | 			assign x = B.y ^ 2'b11 ^ C.z; | ||||||
|  | 			`ASSERT(x == 2) | ||||||
|  | 			`ASSERT(A.x == 2) | ||||||
|  | 			`ASSERT(A.C.z == 1) | ||||||
|  | 			`ASSERT(A.B.y == 0) | ||||||
|  | 			`ASSERT(gen_test9.w == 3) | ||||||
|  | 			`ASSERT(gen_test9.A.x == 2) | ||||||
|  | 			`ASSERT(gen_test9.A.C.z == 1) | ||||||
|  | 			`ASSERT(gen_test9.A.B.y == 0) | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | 
 | ||||||
|  | 	`ASSERT(w == 3) | ||||||
|  | 	`ASSERT(A.x == 2) | ||||||
|  | 	`ASSERT(A.C.z == 1) | ||||||
|  | 	`ASSERT(A.B.y == 0) | ||||||
|  | 	`ASSERT(gen_test9.w == 3) | ||||||
|  | 	`ASSERT(gen_test9.A.x == 2) | ||||||
|  | 	`ASSERT(gen_test9.A.C.z == 1) | ||||||
|  | 	`ASSERT(gen_test9.A.B.y == 0) | ||||||
|  | endmodule | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								tests/simple/local_loop_var.sv
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/simple/local_loop_var.sv
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | module top(out); | ||||||
|  | 	output integer out; | ||||||
|  | 	initial begin | ||||||
|  | 		integer i; | ||||||
|  | 		for (i = 0; i < 5; i = i + 1) | ||||||
|  | 			if (i == 0) | ||||||
|  | 				out = 1; | ||||||
|  | 			else | ||||||
|  | 				out += 2 ** i; | ||||||
|  | 	end | ||||||
|  | endmodule | ||||||
							
								
								
									
										15
									
								
								tests/simple/loop_var_shadow.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tests/simple/loop_var_shadow.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | module top(out); | ||||||
|  | 	genvar i; | ||||||
|  | 	generate | ||||||
|  | 		for (i = 0; i < 2; i = i + 1) begin : loop | ||||||
|  | 			localparam j = i + 1; | ||||||
|  | 			if (1) begin : blk | ||||||
|  | 				localparam i = j + 1; | ||||||
|  | 				wire [i:0] x; | ||||||
|  | 				assign x = 1'sb1; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | 	output wire [63:0] out; | ||||||
|  | 	assign out = {loop[0].blk.x, loop[1].blk.x}; | ||||||
|  | endmodule | ||||||
							
								
								
									
										27
									
								
								tests/simple/named_genblk.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tests/simple/named_genblk.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | `default_nettype none | ||||||
|  | module top; | ||||||
|  | 	generate | ||||||
|  | 		if (1) begin | ||||||
|  | 			wire t; | ||||||
|  | 			begin : foo | ||||||
|  | 				wire x; | ||||||
|  | 			end | ||||||
|  | 			wire u; | ||||||
|  | 		end | ||||||
|  | 		begin : bar | ||||||
|  | 			wire x; | ||||||
|  | 			wire y; | ||||||
|  | 			begin : baz | ||||||
|  | 				wire x; | ||||||
|  | 				wire z; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | 	assign genblk1.t = 1; | ||||||
|  | 	assign genblk1.foo.x = 1; | ||||||
|  | 	assign genblk1.u = 1; | ||||||
|  | 	assign bar.x = 1; | ||||||
|  | 	assign bar.y = 1; | ||||||
|  | 	assign bar.baz.x = 1; | ||||||
|  | 	assign bar.baz.z = 1; | ||||||
|  | endmodule | ||||||
							
								
								
									
										14
									
								
								tests/simple/nested_genblk_resolve.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/simple/nested_genblk_resolve.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | `default_nettype none | ||||||
|  | module top; | ||||||
|  |     generate | ||||||
|  |         if (1) begin | ||||||
|  |             wire x; | ||||||
|  |             genvar i; | ||||||
|  |             for (i = 0; i < 1; i = i + 1) begin | ||||||
|  |                 if (1) begin | ||||||
|  |                     assign x = 1; | ||||||
|  |                 end | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     endgenerate | ||||||
|  | endmodule | ||||||
							
								
								
									
										17
									
								
								tests/simple/unnamed_block_decl.sv
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/simple/unnamed_block_decl.sv
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | module top(z); | ||||||
|  | 	output integer z; | ||||||
|  | 	initial begin | ||||||
|  | 		integer x; | ||||||
|  | 		x = 1; | ||||||
|  | 		begin | ||||||
|  | 			integer y; | ||||||
|  | 			y = x + 1; | ||||||
|  | 			begin | ||||||
|  | 				integer z; | ||||||
|  | 				z = y + 1; | ||||||
|  | 				y = z + 1; | ||||||
|  | 			end | ||||||
|  | 			z = y + 1; | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | endmodule | ||||||
|  | @ -1,13 +1,17 @@ | ||||||
| module test(x, y, z); | `default_nettype none | ||||||
|  | module test; | ||||||
| 	localparam OFF = 0; | 	localparam OFF = 0; | ||||||
| 	generate | 	generate | ||||||
| 		if (OFF) ; | 		if (OFF) ; | ||||||
| 		else input x; | 		else wire x; | ||||||
| 		if (!OFF) input y; | 		if (!OFF) wire y; | ||||||
| 		else ; | 		else ; | ||||||
| 		if (OFF) ; | 		if (OFF) ; | ||||||
| 		else ; | 		else ; | ||||||
| 		if (OFF) ; | 		if (OFF) ; | ||||||
| 		input z; | 		wire z; | ||||||
| 	endgenerate | 	endgenerate | ||||||
|  | 	assign genblk1.x = 0; | ||||||
|  | 	assign genblk2.y = 0; | ||||||
|  | 	assign z = 0; | ||||||
| endmodule | endmodule | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| read_verilog gen_if_null.v | read_verilog gen_if_null.v | ||||||
| select -assert-count 1 test/x | select -assert-count 1 test/genblk1.x | ||||||
| select -assert-count 1 test/y | select -assert-count 1 test/genblk2.y | ||||||
| select -assert-count 1 test/z | select -assert-count 1 test/z | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								tests/verilog/bug2493.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/verilog/bug2493.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | logger -expect error "Failed to detect width for identifier \\genblk1\.y!" 1 | ||||||
|  | read_verilog <<EOT | ||||||
|  | module top1; | ||||||
|  |     wire x; | ||||||
|  |     generate | ||||||
|  |         if (1) begin | ||||||
|  |             mod y(); | ||||||
|  |             assign x = y; | ||||||
|  |         end | ||||||
|  |     endgenerate | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
							
								
								
									
										21
									
								
								tests/verilog/bug656.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/verilog/bug656.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | module top #( | ||||||
|  | 	parameter WIDTH = 6 | ||||||
|  | ) ( | ||||||
|  | 	input [WIDTH-1:0] a_i, | ||||||
|  | 	input [WIDTH-1:0] b_i, | ||||||
|  | 	output [WIDTH-1:0] z_o | ||||||
|  | ); | ||||||
|  | 	genvar g; | ||||||
|  | 	generate | ||||||
|  | 		for (g = 0; g < WIDTH; g = g + 1) begin | ||||||
|  | 			if (g > 2) begin | ||||||
|  | 				wire tmp; | ||||||
|  | 				assign tmp = a_i[g] || b_i[g]; | ||||||
|  | 				assign z_o[g] = tmp; | ||||||
|  | 			end | ||||||
|  | 			else begin | ||||||
|  | 				assign z_o[g] = a_i[g] && b_i[g]; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
							
								
								
									
										13
									
								
								tests/verilog/bug656.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/verilog/bug656.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | read_verilog bug656.v | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 top/a_i | ||||||
|  | select -assert-count 1 top/b_i | ||||||
|  | select -assert-count 1 top/z_o | ||||||
|  | 
 | ||||||
|  | select -assert-none top/genblk1[0].genblk1.tmp | ||||||
|  | select -assert-none top/genblk1[1].genblk1.tmp | ||||||
|  | select -assert-none top/genblk1[2].genblk1.tmp | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 top/genblk1[3].genblk1.tmp | ||||||
|  | select -assert-count 1 top/genblk1[4].genblk1.tmp | ||||||
|  | select -assert-count 1 top/genblk1[5].genblk1.tmp | ||||||
							
								
								
									
										26
									
								
								tests/verilog/genblk_case.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tests/verilog/genblk_case.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | module top; | ||||||
|  | 	parameter YES = 1; | ||||||
|  | 	generate | ||||||
|  | 		if (YES) wire y; | ||||||
|  | 		else wire n; | ||||||
|  | 
 | ||||||
|  | 		if (!YES) wire n; | ||||||
|  | 		else wire y; | ||||||
|  | 
 | ||||||
|  | 		case (YES) | ||||||
|  | 			1: wire y; | ||||||
|  | 			0: wire n; | ||||||
|  | 		endcase | ||||||
|  | 
 | ||||||
|  | 		case (!YES) | ||||||
|  | 			0: wire y; | ||||||
|  | 			1: wire n; | ||||||
|  | 		endcase | ||||||
|  | 
 | ||||||
|  | 		if (YES) wire y; | ||||||
|  | 		else wire n; | ||||||
|  | 
 | ||||||
|  | 		if (!YES) wire n; | ||||||
|  | 		else wire y; | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
							
								
								
									
										15
									
								
								tests/verilog/genblk_case.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tests/verilog/genblk_case.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | read_verilog genblk_case.v | ||||||
|  | 
 | ||||||
|  | select -assert-count 0 top/genblk1.n | ||||||
|  | select -assert-count 0 top/genblk2.n | ||||||
|  | select -assert-count 0 top/genblk3.n | ||||||
|  | select -assert-count 0 top/genblk4.n | ||||||
|  | select -assert-count 0 top/genblk5.n | ||||||
|  | select -assert-count 0 top/genblk6.n | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 top/genblk1.y | ||||||
|  | select -assert-count 1 top/genblk2.y | ||||||
|  | select -assert-count 1 top/genblk3.y | ||||||
|  | select -assert-count 1 top/genblk4.y | ||||||
|  | select -assert-count 1 top/genblk5.y | ||||||
|  | select -assert-count 1 top/genblk6.y | ||||||
							
								
								
									
										11
									
								
								tests/verilog/hidden_decl.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/verilog/hidden_decl.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | logger -expect error "Identifier `\\y' is implicitly declared and `default_nettype is set to none" 1 | ||||||
|  | read_verilog <<EOT | ||||||
|  | `default_nettype none | ||||||
|  | module top1; | ||||||
|  |     wire x; | ||||||
|  |     generate | ||||||
|  |         if (1) wire y; | ||||||
|  |     endgenerate | ||||||
|  |     assign x = y; | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
							
								
								
									
										28
									
								
								tests/verilog/unnamed_block.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tests/verilog/unnamed_block.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | read_verilog <<EOT | ||||||
|  | module top; | ||||||
|  |     initial begin : blk | ||||||
|  |         integer x; | ||||||
|  |     end | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | delete | ||||||
|  | 
 | ||||||
|  | read_verilog -sv <<EOT | ||||||
|  | module top; | ||||||
|  |     initial begin | ||||||
|  |         integer x; | ||||||
|  |     end | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | delete | ||||||
|  | 
 | ||||||
|  | logger -expect error "Local declaration in unnamed block is only supported in SystemVerilog mode!" 1 | ||||||
|  | read_verilog <<EOT | ||||||
|  | module top; | ||||||
|  |     initial begin | ||||||
|  |         integer x; | ||||||
|  |     end | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
							
								
								
									
										39
									
								
								tests/verilog/unnamed_genblk.sv
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								tests/verilog/unnamed_genblk.sv
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | // This test is taken directly from Section 27.6 of IEEE 1800-2017
 | ||||||
|  | 
 | ||||||
|  | module top; | ||||||
|  | 	parameter genblk2 = 0; | ||||||
|  | 	genvar i; | ||||||
|  | 
 | ||||||
|  | 	// The following generate block is implicitly named genblk1
 | ||||||
|  | 
 | ||||||
|  | 	if (genblk2) logic a; // top.genblk1.a
 | ||||||
|  | 	else logic b; // top.genblk1.b
 | ||||||
|  | 
 | ||||||
|  | 	// The following generate block is implicitly named genblk02
 | ||||||
|  | 	// as genblk2 is already a declared identifier
 | ||||||
|  | 
 | ||||||
|  | 	if (genblk2) logic a; // top.genblk02.a
 | ||||||
|  | 	else logic b; // top.genblk02.b
 | ||||||
|  | 
 | ||||||
|  | 	// The following generate block would have been named genblk3
 | ||||||
|  | 	// but is explicitly named g1
 | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < 1; i = i + 1) begin : g1 // block name
 | ||||||
|  | 		// The following generate block is implicitly named genblk1
 | ||||||
|  | 		// as the first nested scope inside g1
 | ||||||
|  | 		if (1) logic a; // top.g1[0].genblk1.a
 | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	// The following generate block is implicitly named genblk4 since
 | ||||||
|  | 	// it belongs to the fourth generate construct in scope "top".
 | ||||||
|  | 	// The previous generate block would have been
 | ||||||
|  | 	// named genblk3 if it had not been explicitly named g1
 | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < 1; i = i + 1) | ||||||
|  | 		// The following generate block is implicitly named genblk1
 | ||||||
|  | 		// as the first nested generate block in genblk4
 | ||||||
|  | 		if (1) logic a; // top.genblk4[0].genblk1.a
 | ||||||
|  | 
 | ||||||
|  | 	// The following generate block is implicitly named genblk5
 | ||||||
|  | 	if (1) logic a; // top.genblk5.a
 | ||||||
|  | endmodule | ||||||
							
								
								
									
										8
									
								
								tests/verilog/unnamed_genblk.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/verilog/unnamed_genblk.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | read_verilog -sv unnamed_genblk.sv | ||||||
|  | select -assert-count 0 top/genblk1.a | ||||||
|  | select -assert-count 1 top/genblk02.b | ||||||
|  | select -assert-count 0 top/genblk1.a | ||||||
|  | select -assert-count 1 top/genblk02.b | ||||||
|  | select -assert-count 1 top/g1[0].genblk1.a | ||||||
|  | select -assert-count 1 top/genblk4[0].genblk1.a | ||||||
|  | select -assert-count 1 top/genblk5.a | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue