mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge pull request #659 from rubund/sv_interfaces
Support for SystemVerilog interfaces and modports
This commit is contained in:
		
						commit
						f24bc1ed0a
					
				
					 11 changed files with 649 additions and 21 deletions
				
			
		|  | @ -2,6 +2,7 @@ | |||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||
|  *  Copyright (C) 2018  Ruben Undheim <ruben.undheim@gmail.com> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted, provided that the above | ||||
|  | @ -905,7 +906,7 @@ RTLIL::Const AstNode::realAsConst(int width) | |||
| // create a new AstModule from an AST_MODULE AST node
 | ||||
| static AstModule* process_module(AstNode *ast, bool defer) | ||||
| { | ||||
| 	log_assert(ast->type == AST_MODULE); | ||||
| 	log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); | ||||
| 
 | ||||
| 	if (defer) | ||||
| 		log("Storing AST representation for module `%s'.\n", ast->str.c_str()); | ||||
|  | @ -916,6 +917,7 @@ static AstModule* process_module(AstNode *ast, bool defer) | |||
| 	current_module->ast = NULL; | ||||
| 	current_module->name = ast->str; | ||||
| 	current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum); | ||||
| 	current_module->set_bool_attribute("\\cells_not_processed"); | ||||
| 
 | ||||
| 	current_ast_mod = ast; | ||||
| 	AstNode *ast_before_simplify = ast->clone(); | ||||
|  | @ -989,6 +991,8 @@ static AstModule* process_module(AstNode *ast, bool defer) | |||
| 		ignoreThisSignalsInInitial = RTLIL::SigSpec(); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ast->type == AST_INTERFACE) | ||||
| 		current_module->set_bool_attribute("\\is_interface"); | ||||
| 	current_module->ast = ast_before_simplify; | ||||
| 	current_module->nolatches = flag_nolatches; | ||||
| 	current_module->nomeminit = flag_nomeminit; | ||||
|  | @ -1031,7 +1035,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump | |||
| 	log_assert(current_ast->type == AST_DESIGN); | ||||
| 	for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) | ||||
| 	{ | ||||
| 		if ((*it)->type == AST_MODULE) | ||||
| 		if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) | ||||
| 		{ | ||||
| 			for (auto n : design->verilog_globals) | ||||
| 				(*it)->children.push_back(n->clone()); | ||||
|  | @ -1083,8 +1087,179 @@ AstModule::~AstModule() | |||
| 		delete ast; | ||||
| } | ||||
| 
 | ||||
| // When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
 | ||||
| // from AST. The interface members are copied into the AST module with the prefix of the interface.
 | ||||
| void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) | ||||
| { | ||||
| 	bool is_top = false; | ||||
| 	AstNode *new_ast = ast->clone(); | ||||
| 	for (auto &intf : local_interfaces) { | ||||
| 		std::string intfname = intf.first.str(); | ||||
| 		RTLIL::Module *intfmodule = intf.second; | ||||
| 		for (auto &wire_it : intfmodule->wires_){ | ||||
| 			AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); | ||||
| 			std::string newname = log_id(wire_it.first); | ||||
| 			newname = intfname + "." + newname; | ||||
| 			wire->str = newname; | ||||
| 			new_ast->children.push_back(wire); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// The old module will be deleted. Rename and mark for deletion:
 | ||||
| 	std::string original_name = this->name.str(); | ||||
| 	std::string changed_name = original_name + "_before_replacing_local_interfaces"; | ||||
| 	design->rename(this, changed_name); | ||||
| 	this->set_bool_attribute("\\to_delete"); | ||||
| 
 | ||||
| 	// Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
 | ||||
| 	// new module.
 | ||||
| 	if (this->get_bool_attribute("\\initial_top")) { | ||||
| 		this->attributes.erase("\\initial_top"); | ||||
| 		is_top = true; | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate RTLIL from AST for the new module and add to the design:
 | ||||
| 	AstModule *newmod = process_module(new_ast, false); | ||||
| 	design->add(newmod); | ||||
| 	RTLIL::Module* mod = design->module(original_name); | ||||
| 	if (is_top) | ||||
| 		mod->set_bool_attribute("\\top"); | ||||
| 
 | ||||
| 	// Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
 | ||||
| 	mod->set_bool_attribute("\\interfaces_replaced_in_module"); | ||||
| } | ||||
| 
 | ||||
| // create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
 | ||||
| // This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
 | ||||
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) | ||||
| { | ||||
| 	AstNode *new_ast = NULL; | ||||
| 	std::string modname = derive_common(design, parameters, &new_ast, mayfail); | ||||
| 
 | ||||
| 	// Since interfaces themselves may be instantiated with different parameters,
 | ||||
| 	// "modname" must also take those into account, so that unique modules
 | ||||
| 	// are derived for any variant of interface connections:
 | ||||
| 	std::string interf_info = ""; | ||||
| 
 | ||||
| 	bool has_interfaces = false; | ||||
| 	for(auto &intf : interfaces) { | ||||
| 		interf_info += log_id(intf.second->name); | ||||
| 		has_interfaces = true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (has_interfaces) | ||||
| 		modname += "$interfaces$" + interf_info; | ||||
| 
 | ||||
| 
 | ||||
| 	if (!design->has(modname)) { | ||||
| 		new_ast->str = modname; | ||||
| 
 | ||||
| 		// Iterate over all interfaces which are ports in this module:
 | ||||
| 		for(auto &intf : interfaces) { | ||||
| 			RTLIL::Module * intfmodule = intf.second; | ||||
| 			std::string intfname = intf.first.str(); | ||||
| 			// Check if a modport applies for the interface port:
 | ||||
| 			AstNode *modport = NULL; | ||||
| 			if (modports.count(intfname) > 0) { | ||||
| 				std::string interface_modport = modports.at(intfname).str(); | ||||
| 				AstModule *ast_module_of_interface = (AstModule*)intfmodule; | ||||
| 				AstNode *ast_node_of_interface = ast_module_of_interface->ast; | ||||
| 				for (auto &ch : ast_node_of_interface->children) { | ||||
| 					if (ch->type == AST_MODPORT) { | ||||
| 						if (ch->str == interface_modport) { // Modport found
 | ||||
| 							modport = ch; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			// Iterate over all wires in the interface and add them to the module:
 | ||||
| 			for (auto &wire_it : intfmodule->wires_){ | ||||
| 				AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); | ||||
| 				std::string origname = log_id(wire_it.first); | ||||
| 				std::string newname = intfname + "." + origname; | ||||
| 				wire->str = newname; | ||||
| 				if (modport != NULL) { | ||||
| 					bool found_in_modport = false; | ||||
| 					// Search for the current wire in the wire list for the current modport
 | ||||
| 					for (auto &ch : modport->children) { | ||||
| 						if (ch->type == AST_MODPORTMEMBER) { | ||||
| 							std::string compare_name = "\\" + origname; | ||||
| 							if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output
 | ||||
| 								found_in_modport = true; | ||||
| 								wire->is_input = ch->is_input; | ||||
| 								wire->is_output = ch->is_output; | ||||
| 								break; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 					if (found_in_modport) { | ||||
| 						new_ast->children.push_back(wire); | ||||
| 					} | ||||
| 					else { // If not found in modport, do not create port
 | ||||
| 						delete wire; | ||||
| 					} | ||||
| 				} | ||||
| 				else { // If no modport, set inout
 | ||||
| 					wire->is_input = true; | ||||
| 					wire->is_output = true; | ||||
| 					new_ast->children.push_back(wire); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		design->add(process_module(new_ast, false)); | ||||
| 		design->module(modname)->check(); | ||||
| 
 | ||||
| 		RTLIL::Module* mod = design->module(modname); | ||||
| 
 | ||||
| 		// Now that the interfaces have been exploded, we can delete the dummy port related to every interface.
 | ||||
| 		for(auto &intf : interfaces) { | ||||
| 			if(mod->wires_.count(intf.first)) { | ||||
| 				mod->wires_.erase(intf.first); | ||||
| 				mod->fixup_ports(); | ||||
| 				// We copy the cell of the interface to the sub-module such that it can further be found if it is propagated
 | ||||
| 				// down to sub-sub-modules etc.
 | ||||
| 				RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name); | ||||
| 				new_subcell->set_bool_attribute("\\is_interface"); | ||||
| 			} | ||||
| 			else { | ||||
| 				log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str()); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module':
 | ||||
| 		if (interfaces.size() > 0) { | ||||
| 			mod->set_bool_attribute("\\interfaces_replaced_in_module"); | ||||
| 		} | ||||
| 
 | ||||
| 	} else { | ||||
| 		log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); | ||||
| 	} | ||||
| 
 | ||||
| 	delete new_ast; | ||||
| 	return modname; | ||||
| } | ||||
| 
 | ||||
| // create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
 | ||||
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) | ||||
| { | ||||
| 	AstNode *new_ast = NULL; | ||||
| 	std::string modname = derive_common(design, parameters, &new_ast, mayfail); | ||||
| 
 | ||||
| 	if (!design->has(modname)) { | ||||
| 		new_ast->str = modname; | ||||
| 		design->add(process_module(new_ast, false)); | ||||
| 		design->module(modname)->check(); | ||||
| 	} else { | ||||
| 		log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); | ||||
| 	} | ||||
| 
 | ||||
| 	delete new_ast; | ||||
| 	return modname; | ||||
| } | ||||
| 
 | ||||
| // create a new parametric module (when needed) and return the name of the generated module
 | ||||
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool) | ||||
| std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool) | ||||
| { | ||||
| 	std::string stripped_name = name.str(); | ||||
| 
 | ||||
|  | @ -1156,15 +1331,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R | |||
| 	else | ||||
| 		modname = "$paramod" + stripped_name + para_info; | ||||
| 
 | ||||
| 	if (!design->has(modname)) { | ||||
| 		new_ast->str = modname; | ||||
| 		design->add(process_module(new_ast, false)); | ||||
| 		design->module(modname)->check(); | ||||
| 	} else { | ||||
| 		log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); | ||||
| 	} | ||||
| 
 | ||||
| 	delete new_ast; | ||||
| 	(*new_ast_out) = new_ast; | ||||
| 	return modname; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -142,6 +142,11 @@ namespace AST | |||
| 		AST_NEGEDGE, | ||||
| 		AST_EDGE, | ||||
| 
 | ||||
| 		AST_INTERFACE, | ||||
| 		AST_INTERFACEPORT, | ||||
| 		AST_INTERFACEPORTTYPE, | ||||
| 		AST_MODPORT, | ||||
| 		AST_MODPORTMEMBER, | ||||
| 		AST_PACKAGE | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -284,6 +289,9 @@ namespace AST | |||
| 		bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; | ||||
| 		~AstModule() YS_OVERRIDE; | ||||
| 		RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; | ||||
| 		RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; | ||||
| 		std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail); | ||||
| 		void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE; | ||||
| 		RTLIL::Module *clone() const YS_OVERRIDE; | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -853,6 +853,52 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 	case AST_GENIF: | ||||
| 	case AST_GENCASE: | ||||
| 	case AST_PACKAGE: | ||||
| 	case AST_MODPORT: | ||||
| 	case AST_MODPORTMEMBER: | ||||
| 		break; | ||||
| 	case AST_INTERFACEPORT: { | ||||
| 		// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
 | ||||
| 		// 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["\\src"] = stringf("%s:%d", filename.c_str(), linenum); | ||||
| 		wire->start_offset = 0; | ||||
| 		wire->port_id = port_id; | ||||
| 		wire->port_input = true; | ||||
| 		wire->port_output = true; | ||||
| 		wire->set_bool_attribute("\\is_interface"); | ||||
| 		if (children.size() > 0) { | ||||
| 			for(size_t i=0; i<children.size();i++) { | ||||
| 				if(children[i]->type == AST_INTERFACEPORTTYPE) { | ||||
| 					std::string name_type = children[i]->str; | ||||
| 					size_t ndots = std::count(name_type.begin(), name_type.end(), '.'); | ||||
| 					// Separate the interface instance name from any modports:
 | ||||
| 					if (ndots == 0) { // Does not have modport
 | ||||
| 						wire->attributes["\\interface_type"] = name_type; | ||||
| 					} | ||||
| 					else { | ||||
| 						std::stringstream name_type_stream(name_type); | ||||
| 						std::string segment; | ||||
| 						std::vector<std::string> seglist; | ||||
| 						while(std::getline(name_type_stream, segment, '.')) { | ||||
| 							seglist.push_back(segment); | ||||
| 						} | ||||
| 						if (ndots == 1) { // Has modport
 | ||||
| 							wire->attributes["\\interface_type"] = seglist[0]; | ||||
| 							wire->attributes["\\interface_modport"] = seglist[1]; | ||||
| 						} | ||||
| 						else { // Erroneous port type
 | ||||
| 							log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); | ||||
| 						} | ||||
| 					} | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		wire->upto = 0; | ||||
| 		} | ||||
| 		break; | ||||
| 	case AST_INTERFACEPORTTYPE: | ||||
| 		break; | ||||
| 
 | ||||
| 	// remember the parameter, needed for example in techmap
 | ||||
|  | @ -949,6 +995,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 		{ | ||||
| 			RTLIL::Wire *wire = NULL; | ||||
| 			RTLIL::SigChunk chunk; | ||||
| 			bool is_interface = false; | ||||
| 
 | ||||
| 			int add_undef_bits_msb = 0; | ||||
| 			int add_undef_bits_lsb = 0; | ||||
|  | @ -969,15 +1016,41 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 				chunk = RTLIL::Const(id2ast->children[0]->bits); | ||||
| 				goto use_const_chunk; | ||||
| 			} | ||||
| 			else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE && | ||||
| 					id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0) | ||||
| 			else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { | ||||
| 				RTLIL::Wire *current_wire = current_module->wire(str); | ||||
| 				if (current_wire->get_bool_attribute("\\is_interface")) | ||||
| 					is_interface = true; | ||||
| 				// Ignore
 | ||||
| 			} | ||||
| 			// If an identifier is found that is not already known, assume that it is an interface:
 | ||||
| 			else if (1) { // FIXME: Check if sv_mode first?
 | ||||
| 				is_interface = true; | ||||
| 			} | ||||
| 			else { | ||||
| 				log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", | ||||
| 						str.c_str()); | ||||
| 			} | ||||
| 
 | ||||
| 			if (id2ast->type == AST_MEMORY) | ||||
| 				log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", | ||||
| 					       str.c_str()); | ||||
| 
 | ||||
| 			// If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface'
 | ||||
| 			// This makes it possible for the hierarchy pass to see what are interface connections and then replace them
 | ||||
| 			// with the individual signals:
 | ||||
| 			if (is_interface) { | ||||
| 				RTLIL::Wire *dummy_wire; | ||||
| 				std::string dummy_wire_name = "$dummywireforinterface" + str; | ||||
| 				if (current_module->wires_.count(dummy_wire_name)) | ||||
| 					dummy_wire = current_module->wires_[dummy_wire_name]; | ||||
| 				else { | ||||
| 					dummy_wire = current_module->addWire(dummy_wire_name); | ||||
| 					dummy_wire->set_bool_attribute("\\is_interface"); | ||||
| 				} | ||||
| 				RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire); | ||||
| 				return tmp; | ||||
| 			} | ||||
| 
 | ||||
| 			wire = current_module->wires_[str]; | ||||
| 			chunk.wire = wire; | ||||
| 			chunk.width = wire->width; | ||||
|  | @ -1423,6 +1496,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 
 | ||||
| 			RTLIL::Cell *cell = current_module->addCell(str, ""); | ||||
| 			cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); | ||||
| 			// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
 | ||||
| 			cell->set_bool_attribute("\\module_not_derived"); | ||||
| 
 | ||||
| 			for (auto it = children.begin(); it != children.end(); it++) { | ||||
| 				AstNode *child = *it; | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 
 | ||||
| 	if (stage == 0) | ||||
| 	{ | ||||
| 		log_assert(type == AST_MODULE); | ||||
| 		log_assert(type == AST_MODULE || type == AST_INTERFACE); | ||||
| 		last_blocking_assignment_warn = pair<string, int>(); | ||||
| 
 | ||||
| 		deep_recursion_warning = true; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue