diff options
| author | Clifford Wolf <clifford@clifford.at> | 2018-10-18 10:58:47 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-10-18 10:58:47 +0200 | 
| commit | f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c (patch) | |
| tree | 1778829a6932d18730a3a085a80a65205189c7ba /frontends/ast | |
| parent | 24a5c6585678f89058382fe2c3f36b821b419e90 (diff) | |
| parent | 736105b0468f9468f00915cad60949535ce5a496 (diff) | |
| download | yosys-f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c.tar.gz yosys-f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c.tar.bz2 yosys-f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c.zip  | |
Merge pull request #659 from rubund/sv_interfaces
Support for SystemVerilog interfaces and modports
Diffstat (limited to 'frontends/ast')
| -rw-r--r-- | frontends/ast/ast.cc | 190 | ||||
| -rw-r--r-- | frontends/ast/ast.h | 8 | ||||
| -rw-r--r-- | frontends/ast/genrtlil.cc | 79 | ||||
| -rw-r--r-- | frontends/ast/simplify.cc | 2 | 
4 files changed, 265 insertions, 14 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index e79be953a..7600e2912 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -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;  } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 7e97bdb3b..8187b1ac6 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -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;  	}; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index c9345ff08..32b9af6e9 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -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; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index dca9da198..e56a62563 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -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;  | 
