diff options
| author | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 | 
|---|---|---|
| committer | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 | 
| commit | d3c67ad9b61f602de1100cd264efd227dcacb417 (patch) | |
| tree | 88c462c53bdab128cd1edbded42483772f82612a /frontends/ast | |
| parent | b783dbe148e6d246ebd107c0913de2989ab5af48 (diff) | |
| parent | 13117bb346dd02d2345f716b4403239aebe3d0e2 (diff) | |
| download | yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.gz yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.bz2 yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.zip  | |
Merge branch 'master' of https://github.com/cliffordwolf/yosys into btor
added case for memwr cell that is used in muxes (same cell is used more than one time)
corrected bug for xnor and logic_not
added pmux cell translation
Conflicts:
	backends/btor/btor.cc
Diffstat (limited to 'frontends/ast')
| -rw-r--r-- | frontends/ast/Makefile.inc | 1 | ||||
| -rw-r--r-- | frontends/ast/ast.cc | 323 | ||||
| -rw-r--r-- | frontends/ast/ast.h | 52 | ||||
| -rw-r--r-- | frontends/ast/dpicall.cc | 140 | ||||
| -rw-r--r-- | frontends/ast/genrtlil.cc | 618 | ||||
| -rw-r--r-- | frontends/ast/simplify.cc | 1123 | 
6 files changed, 1691 insertions, 566 deletions
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc index 993ead928..91d917c91 100644 --- a/frontends/ast/Makefile.inc +++ b/frontends/ast/Makefile.inc @@ -2,4 +2,5 @@  OBJS += frontends/ast/ast.o  OBJS += frontends/ast/simplify.o  OBJS += frontends/ast/genrtlil.o +OBJS += frontends/ast/dpicall.o diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 96608ae37..1e43875ae 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -32,7 +32,9 @@  #include <sstream>  #include <stdarg.h> -#include <assert.h> +#include <math.h> + +YOSYS_NAMESPACE_BEGIN  using namespace AST;  using namespace AST_INTERNAL; @@ -46,11 +48,10 @@ namespace AST {  // instanciate global variables (private API)  namespace AST_INTERNAL { -	bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells; +	bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;  	AstNode *current_ast, *current_ast_mod;  	std::map<std::string, AstNode*> current_scope; -	RTLIL::SigSpec *genRTLIL_subst_from = NULL; -	RTLIL::SigSpec *genRTLIL_subst_to = NULL; +	const std::map<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;  	RTLIL::SigSpec ignoreThisSignalsInInitial;  	AstNode *current_top_block, *current_block, *current_block_child;  	AstModule *current_module; @@ -67,6 +68,7 @@ std::string AST::type2str(AstNodeType type)  	X(AST_MODULE)  	X(AST_TASK)  	X(AST_FUNCTION) +	X(AST_DPI_FUNCTION)  	X(AST_WIRE)  	X(AST_MEMORY)  	X(AST_AUTOWIRE) @@ -76,7 +78,9 @@ std::string AST::type2str(AstNodeType type)  	X(AST_PARASET)  	X(AST_ARGUMENT)  	X(AST_RANGE) +	X(AST_MULTIRANGE)  	X(AST_CONSTANT) +	X(AST_REALVALUE)  	X(AST_CELLTYPE)  	X(AST_IDENTIFIER)  	X(AST_PREFIX) @@ -127,6 +131,7 @@ std::string AST::type2str(AstNodeType type)  	X(AST_ASSIGN)  	X(AST_CELL)  	X(AST_PRIMITIVE) +	X(AST_CELLARRAY)  	X(AST_ALWAYS)  	X(AST_INITIAL)  	X(AST_BLOCK) @@ -136,6 +141,8 @@ std::string AST::type2str(AstNodeType type)  	X(AST_COND)  	X(AST_DEFAULT)  	X(AST_FOR) +	X(AST_WHILE) +	X(AST_REPEAT)  	X(AST_GENVAR)  	X(AST_GENFOR)  	X(AST_GENIF) @@ -177,10 +184,12 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2)  	is_signed = false;  	is_string = false;  	range_valid = false; +	range_swapped = false;  	port_id = 0;  	range_left = -1;  	range_right = 0;  	integer = 0; +	realvalue = 0;  	id2ast = NULL;  	basic_prep = false; @@ -243,6 +252,12 @@ void AstNode::dumpAst(FILE *f, std::string indent)  	std::string type_name = type2str(type);  	fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum); + +	if (id2ast) +		fprintf(f, " [%p -> %p]", this, id2ast); +	else +		fprintf(f, " [%p]", this); +  	if (!str.empty())  		fprintf(f, " str='%s'", str.c_str());  	if (!bits.empty()) { @@ -265,9 +280,17 @@ void AstNode::dumpAst(FILE *f, std::string indent)  	if (port_id > 0)  		fprintf(f, " port=%d", port_id);  	if (range_valid || range_left != -1 || range_right != 0) -		fprintf(f, " range=[%d:%d]%s", range_left, range_right, range_valid ? "" : "!"); +		fprintf(f, " %srange=[%d:%d]%s", range_swapped ? "swapped_" : "", range_left, range_right, range_valid ? "" : "!");  	if (integer != 0)  		fprintf(f, " int=%u", (int)integer); +	if (realvalue != 0) +		fprintf(f, " real=%e", realvalue); +	if (!multirange_dimensions.empty()) { +		fprintf(f, " multirange=["); +		for (int v : multirange_dimensions) +			fprintf(f, " %d", v); +		fprintf(f, " ]"); +	}  	fprintf(f, "\n");  	for (auto &it : attributes) { @@ -309,7 +332,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent)  	}  	for (auto &it : attributes) { -		fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first).c_str()); +		fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first.str()).c_str());  		it.second->dumpVlog(f, "");  		fprintf(f, " *)%s", indent.empty() ? "" : "\n");  	} @@ -451,6 +474,10 @@ void AstNode::dumpVlog(FILE *f, std::string indent)  			fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str());  		break; +	case AST_REALVALUE: +		fprintf(f, "%e", realvalue); +		break; +  	case AST_BLOCK:  		if (children.size() == 1) {  			children[0]->dumpVlog(f, indent); @@ -603,6 +630,8 @@ bool AstNode::operator==(const AstNode &other) const  		return false;  	if (range_valid != other.range_valid)  		return false; +	if (range_swapped != other.range_swapped) +		return false;  	if (port_id != other.port_id)  		return false;  	if (range_left != other.range_left) @@ -694,6 +723,14 @@ AstNode *AstNode::mkconst_str(const std::string &str)  	return node;  } +bool AstNode::bits_only_01() +{ +	for (auto bit : bits) +		if (bit != RTLIL::S0 && bit != RTLIL::S1) +			return false; +	return true; +} +  RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed)  {  	std::vector<RTLIL::State> bits = this->bits; @@ -746,11 +783,94 @@ bool AstNode::asBool()  	return false;  } +int AstNode::isConst() +{ +	if (type == AST_CONSTANT) +		return 1; +	if (type == AST_REALVALUE) +		return 2; +	return 0; +} + +uint64_t AstNode::asInt(bool is_signed) +{ +	if (type == AST_CONSTANT) +	{ +		RTLIL::Const v = bitsAsConst(64, is_signed); +		uint64_t ret = 0; + +		for (int i = 0; i < 64; i++) +			if (v.bits.at(i) == RTLIL::State::S1) +				ret |= uint64_t(1) << i; + +		return ret; +	} + +	if (type == AST_REALVALUE) +		return realvalue; + +	log_abort(); +} + +double AstNode::asReal(bool is_signed) +{ +	if (type == AST_CONSTANT) +	{ +		RTLIL::Const val(bits); + +		bool is_negative = is_signed && val.bits.back() == RTLIL::State::S1; +		if (is_negative) +			val = const_neg(val, val, false, false, val.bits.size()); + +		double v = 0; +		for (size_t i = 0; i < val.bits.size(); i++) +			// IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in +			// the net or the variable shall be treated as zero upon conversion. +			if (val.bits.at(i) == RTLIL::State::S1) +				v += exp2(i); +		if (is_negative) +			v *= -1; + +		return v; +	} + +	if (type == AST_REALVALUE) +		return realvalue; + +	log_abort(); +} + +RTLIL::Const AstNode::realAsConst(int width) +{ +	double v = round(realvalue); +	RTLIL::Const result; +#ifdef EMSCRIPTEN +	if (!isfinite(v)) { +#else +	if (!std::isfinite(v)) { +#endif +		result.bits = std::vector<RTLIL::State>(width, RTLIL::State::Sx); +	} else { +		bool is_negative = v < 0; +		if (is_negative) +			v *= -1; +		for (int i = 0; i < width; i++, v /= 2) +			result.bits.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); +		if (is_negative) +			result = const_neg(result, result, false, false, result.bits.size()); +	} +	return result; +} +  // create a new AstModule from an AST_MODULE AST node -static AstModule* process_module(AstNode *ast) +static AstModule* process_module(AstNode *ast, bool defer)  { -	assert(ast->type == AST_MODULE); -	log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); +	log_assert(ast->type == AST_MODULE); + +	if (defer) +		log("Storing AST representation for module `%s'.\n", ast->str.c_str()); +	else +		log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str());  	current_module = new AstModule;  	current_module->ast = NULL; @@ -766,60 +886,63 @@ static AstModule* process_module(AstNode *ast)  		log("--- END OF AST DUMP ---\n");  	} -	while (ast->simplify(!flag_noopt, false, false, 0, -1, false)) { } +	if (!defer) +	{ +		while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } -	if (flag_dump_ast2) { -		log("Dumping verilog AST after simplification:\n"); -		ast->dumpAst(NULL, "    "); -		log("--- END OF AST DUMP ---\n"); -	} +		if (flag_dump_ast2) { +			log("Dumping verilog AST after simplification:\n"); +			ast->dumpAst(NULL, "    "); +			log("--- END OF AST DUMP ---\n"); +		} -	if (flag_dump_vlog) { -		log("Dumping verilog AST (as requested by dump_vlog option):\n"); -		ast->dumpVlog(NULL, "    "); -		log("--- END OF AST DUMP ---\n"); -	} +		if (flag_dump_vlog) { +			log("Dumping verilog AST (as requested by dump_vlog option):\n"); +			ast->dumpVlog(NULL, "    "); +			log("--- END OF AST DUMP ---\n"); +		} -	if (flag_lib) { -		std::vector<AstNode*> new_children; -		for (auto child : ast->children) { -			if (child->type == AST_WIRE && (child->is_input || child->is_output)) -				new_children.push_back(child); -			else -				delete child; +		if (flag_lib) { +			std::vector<AstNode*> new_children; +			for (auto child : ast->children) { +				if (child->type == AST_WIRE && (child->is_input || child->is_output)) +					new_children.push_back(child); +				else +					delete child; +			} +			ast->children.swap(new_children); +			ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);  		} -		ast->children.swap(new_children); -		ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); -	} -	ignoreThisSignalsInInitial = RTLIL::SigSpec(); +		ignoreThisSignalsInInitial = RTLIL::SigSpec(); -	for (auto &attr : ast->attributes) { -		if (attr.second->type != AST_CONSTANT) -			log_error("Attribute `%s' with non-constant value at %s:%d!\n", -					attr.first.c_str(), ast->filename.c_str(), ast->linenum); -		current_module->attributes[attr.first] = attr.second->asAttrConst(); -	} -	for (size_t i = 0; i < ast->children.size(); i++) { -		AstNode *node = ast->children[i]; -		if (node->type == AST_WIRE || node->type == AST_MEMORY) -			node->genRTLIL(); -	} -	for (size_t i = 0; i < ast->children.size(); i++) { -		AstNode *node = ast->children[i]; -		if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) -			node->genRTLIL(); -	} +		for (auto &attr : ast->attributes) { +			if (attr.second->type != AST_CONSTANT) +				log_error("Attribute `%s' with non-constant value at %s:%d!\n", +						attr.first.c_str(), ast->filename.c_str(), ast->linenum); +			current_module->attributes[attr.first] = attr.second->asAttrConst(); +		} +		for (size_t i = 0; i < ast->children.size(); i++) { +			AstNode *node = ast->children[i]; +			if (node->type == AST_WIRE || node->type == AST_MEMORY) +				node->genRTLIL(); +		} +		for (size_t i = 0; i < ast->children.size(); i++) { +			AstNode *node = ast->children[i]; +			if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) +				node->genRTLIL(); +		} -	ignoreThisSignalsInInitial.sort_and_unify(); +		ignoreThisSignalsInInitial.sort_and_unify(); -	for (size_t i = 0; i < ast->children.size(); i++) { -		AstNode *node = ast->children[i]; -		if (node->type == AST_INITIAL) -			node->genRTLIL(); -	} +		for (size_t i = 0; i < ast->children.size(); i++) { +			AstNode *node = ast->children[i]; +			if (node->type == AST_INITIAL) +				node->genRTLIL(); +		} -	ignoreThisSignalsInInitial = RTLIL::SigSpec(); +		ignoreThisSignalsInInitial = RTLIL::SigSpec(); +	}  	current_module->ast = ast_before_simplify;  	current_module->nolatches = flag_nolatches; @@ -828,11 +951,13 @@ static AstModule* process_module(AstNode *ast)  	current_module->lib = flag_lib;  	current_module->noopt = flag_noopt;  	current_module->icells = flag_icells; +	current_module->autowire = flag_autowire; +	current_module->fixup_ports();  	return current_module;  }  // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire)  {  	current_ast = ast;  	flag_dump_ast1 = dump_ast1; @@ -844,18 +969,37 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump  	flag_lib = lib;  	flag_noopt = noopt;  	flag_icells = icells; +	flag_autowire = autowire; + +	std::vector<AstNode*> global_decls; -	assert(current_ast->type == AST_DESIGN); -	for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { -		if (design->modules.count((*it)->str) != 0) { -			if (!ignore_redef) -				log_error("Re-definition of module `%s' at %s:%d!\n", +	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) +		{ +			for (auto n : global_decls) +				(*it)->children.push_back(n->clone()); + +			if (flag_icells && (*it)->str.substr(0, 2) == "\\$") +				(*it)->str = (*it)->str.substr(1); + +			if (defer) +				(*it)->str = "$abstract" + (*it)->str; + +			if (design->has((*it)->str)) { +				if (!ignore_redef) +					log_error("Re-definition of module `%s' at %s:%d!\n", +							(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); +				log("Ignoring re-definition of module `%s' at %s:%d!\n",  						(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); -			log_error("Ignoring re-definition of module `%s' at %s:%d!\n", -					(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); -			continue; +				continue; +			} + +			design->add(process_module(*it, defer));  		} -		design->modules[(*it)->str] = process_module(*it); +		else +			global_decls.push_back(*it);  	}  } @@ -869,7 +1013,12 @@ AstModule::~AstModule()  // create a new parametric module (when needed) and return the name of the generated module  RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters)  { -	log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", name.c_str()); +	std::string stripped_name = name.str(); + +	if (stripped_name.substr(0, 9) == "$abstract") +		stripped_name = stripped_name.substr(9); + +	log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());  	current_ast = NULL;  	flag_dump_ast1 = false; @@ -881,16 +1030,14 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin  	flag_lib = lib;  	flag_noopt = noopt;  	flag_icells = icells; +	flag_autowire = autowire;  	use_internal_line_num();  	std::string para_info; -	std::vector<unsigned char> hash_data; -	hash_data.insert(hash_data.end(), name.begin(), name.end()); -	hash_data.push_back(0); -  	AstNode *new_ast = ast->clone();  	int para_counter = 0; +	int orig_parameters_n = parameters.size();  	for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) {  		AstNode *child = *it;  		if (child->type != AST_PARAMETER) @@ -903,10 +1050,6 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin  			para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));  			delete child->children.at(0);  			child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); -			hash_data.insert(hash_data.end(), child->str.begin(), child->str.end()); -			hash_data.push_back(0); -			hash_data.insert(hash_data.end(), parameters[para_id].bits.begin(), parameters[para_id].bits.end()); -			hash_data.push_back(0xff);  			parameters.erase(para_id);  			continue;  		} @@ -917,33 +1060,21 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin  		}  	}  	if (parameters.size() > 0) -		log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), name.c_str()); +		log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), stripped_name.c_str());  	std::string modname; -	if (para_info.size() > 60) -	{ -		unsigned char hash[20]; -		unsigned char *hash_data2 = new unsigned char[hash_data.size()]; -		for (size_t i = 0; i < hash_data.size(); i++) -			hash_data2[i] = hash_data[i]; -		sha1::calc(hash_data2, hash_data.size(), hash); -		delete[] hash_data2; - -		char hexstring[41]; -		sha1::toHexString(hash, hexstring); - -		modname = "$paramod$" + std::string(hexstring) + name; -	} +	if (orig_parameters_n == 0) +		modname = stripped_name; +	else if (para_info.size() > 60) +		modname = "$paramod$" + sha1(para_info) + stripped_name;  	else -	{ -		modname = "$paramod" + name + para_info; -	} +		modname = "$paramod" + stripped_name + para_info; -	if (design->modules.count(modname) == 0) { +	if (!design->has(modname)) {  		new_ast->str = modname; -		design->modules[modname] = process_module(new_ast); -		design->modules[modname]->check(); +		design->add(process_module(new_ast, false)); +		design->module(modname)->check();  	} else {  		log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());  	} @@ -955,6 +1086,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin  RTLIL::Module *AstModule::clone() const  {  	AstModule *new_mod = new AstModule; +	new_mod->name = name;  	cloneInto(new_mod);  	new_mod->ast = ast->clone(); @@ -964,6 +1096,7 @@ RTLIL::Module *AstModule::clone() const  	new_mod->lib = lib;  	new_mod->noopt = noopt;  	new_mod->icells = icells; +	new_mod->autowire = autowire;  	return new_mod;  } @@ -986,3 +1119,5 @@ void AST::use_internal_line_num()  	get_line_num = &internal_get_line_num;  } +YOSYS_NAMESPACE_END + diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 01702c3cf..0a4016736 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -33,6 +33,8 @@  #include <stdint.h>  #include <set> +YOSYS_NAMESPACE_BEGIN +  namespace AST  {  	// all node types, type2str() must be extended @@ -44,6 +46,7 @@ namespace AST  		AST_MODULE,  		AST_TASK,  		AST_FUNCTION, +		AST_DPI_FUNCTION,  		AST_WIRE,  		AST_MEMORY, @@ -54,7 +57,9 @@ namespace AST  		AST_PARASET,  		AST_ARGUMENT,  		AST_RANGE, +		AST_MULTIRANGE,  		AST_CONSTANT, +		AST_REALVALUE,  		AST_CELLTYPE,  		AST_IDENTIFIER,  		AST_PREFIX, @@ -107,6 +112,7 @@ namespace AST  		AST_ASSIGN,  		AST_CELL,  		AST_PRIMITIVE, +		AST_CELLARRAY,  		AST_ALWAYS,  		AST_INITIAL,  		AST_BLOCK, @@ -116,6 +122,8 @@ namespace AST  		AST_COND,  		AST_DEFAULT,  		AST_FOR, +		AST_WHILE, +		AST_REPEAT,  		AST_GENVAR,  		AST_GENFOR, @@ -147,9 +155,13 @@ namespace AST  		// node content - most of it is unused in most node types  		std::string str;  		std::vector<RTLIL::State> bits; -		bool is_input, is_output, is_reg, is_signed, is_string, range_valid; +		bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped;  		int port_id, range_left, range_right;  		uint32_t integer; +		double realvalue; + +		// if this is a multirange memory then this vector contains offset and length of each dimension +		std::vector<int> multirange_dimensions;  		// this is set by simplify and used during RTLIL generation  		AstNode *id2ast; @@ -183,6 +195,7 @@ namespace AST  			MEM2REG_FL_SET_ELSE  = 0x00000400,  			MEM2REG_FL_SET_ASYNC = 0x00000800,  			MEM2REG_FL_EQ2       = 0x00001000, +			MEM2REG_FL_CMPLX_LHS = 0x00002000,  			/* proc flags */  			MEM2REG_FL_EQ1       = 0x01000000, @@ -190,27 +203,33 @@ namespace AST  		// simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.  		// it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() -		bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint); +		bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);  		void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); -		void replace_ids(std::map<std::string, std::string> &rules); +		void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules);  		void mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places,  				std::map<AstNode*, uint32_t> &mem2reg_flags, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);  		void mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block);  		void meminfo(int &mem_width, int &mem_size, int &addr_bits); +		// additional functionality for evaluating constant functions +		struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; +		bool has_const_only_constructs(bool &recommend_const_eval); +		void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); +		AstNode *eval_const_function(AstNode *fcall); +  		// create a human-readable text representation of the AST (for debugging)  		void dumpAst(FILE *f, std::string indent);  		void dumpVlog(FILE *f, std::string indent);  		// used by genRTLIL() for detecting expression width and sign -		void detectSignWidthWorker(int &width_hint, bool &sign_hint); -		void detectSignWidth(int &width_hint, bool &sign_hint); +		void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL); +		void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);  		// create RTLIL code for this AST node  		// for expressions the resulting signal vector is returned  		// all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module  		RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false); -		RTLIL::SigSpec genWidthRTLIL(int width, RTLIL::SigSpec *subst_from = NULL,  RTLIL::SigSpec *subst_to = NULL); +		RTLIL::SigSpec genWidthRTLIL(int width, const std::map<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);  		// compare AST nodes  		bool operator==(const AstNode &other) const; @@ -228,17 +247,24 @@ namespace AST  		RTLIL::Const bitsAsConst(int width = -1);  		RTLIL::Const asAttrConst();  		RTLIL::Const asParaConst(); +		uint64_t asInt(bool is_signed); +		bool bits_only_01();  		bool asBool(); + +		// helper functions for real valued const eval +		int isConst(); // return '1' for AST_CONSTANT and '2' for AST_REALVALUE +		double asReal(bool is_signed); +		RTLIL::Const realAsConst(int width);  	};  	// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code -	void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1 = false, bool dump_ast2 = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false, bool mem2reg = false, bool lib = false, bool noopt = false, bool icells = false, bool ignore_redef = false); +	void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire);  	// parametric modules are supported directly by the AST library  	// therfore we need our own derivate of RTLIL::Module with overloaded virtual functions  	struct AstModule : RTLIL::Module {  		AstNode *ast; -		bool nolatches, nomem2reg, mem2reg, lib, noopt, icells; +		bool nolatches, nomem2reg, mem2reg, lib, noopt, icells, autowire;  		virtual ~AstModule();  		virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters);  		virtual RTLIL::Module *clone() const; @@ -254,18 +280,24 @@ namespace AST  	// set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive  	// to control the filename and linenum properties of new nodes not generated by a frontend parser)  	void use_internal_line_num(); + +	// call a DPI function +	AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args);  }  namespace AST_INTERNAL  {  	// internal state variables -	extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells; +	extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;  	extern AST::AstNode *current_ast, *current_ast_mod;  	extern std::map<std::string, AST::AstNode*> current_scope; -	extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to, ignoreThisSignalsInInitial; +	extern const std::map<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; +	extern RTLIL::SigSpec ignoreThisSignalsInInitial;  	extern AST::AstNode *current_top_block, *current_block, *current_block_child;  	extern AST::AstModule *current_module;  	struct ProcessGenerator;  } +YOSYS_NAMESPACE_END +  #endif diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc new file mode 100644 index 000000000..2eb104fa0 --- /dev/null +++ b/frontends/ast/dpicall.cc @@ -0,0 +1,140 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *   + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + *   + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "ast.h" + +#ifdef YOSYS_ENABLE_PLUGINS + +#include <dlfcn.h> +#include <ffi.h> + +typedef void (*ffi_fptr) (); + +static ffi_fptr resolve_fn (std::string symbol_name) +{ +	if (symbol_name.find(':') != std::string::npos) +	{ +		int pos = symbol_name.find(':'); +		std::string plugin_name = symbol_name.substr(0, pos); +		std::string real_symbol_name = symbol_name.substr(pos+1); + +		while (loaded_plugin_aliases.count(plugin_name)) +			plugin_name = loaded_plugin_aliases.at(plugin_name); + +		if (loaded_plugins.count(plugin_name) == 0) +			log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name.c_str(), plugin_name.c_str()); + +		void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str()); + +		if (symbol == nullptr) +			log_error("unable to resolve '%s': can't find symbol `%s' in plugin `%s'\n", +					symbol_name.c_str(), real_symbol_name.c_str(), plugin_name.c_str()); + +		return (ffi_fptr) symbol; +	} + +	for (auto &it : loaded_plugins) { +		void *symbol = dlsym(it.second, symbol_name.c_str()); +		if (symbol != nullptr) +			return (ffi_fptr) symbol; +	} + +	void *symbol = dlsym(RTLD_DEFAULT, symbol_name.c_str()); +	if (symbol != nullptr) +		return (ffi_fptr) symbol; + +	log_error("unable to resolve '%s'.\n", symbol_name.c_str()); +} + +AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args) +{ +	AST::AstNode *newNode = nullptr; +	union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; +	ffi_type *types [args.size() + 1]; +	void *values [args.size() + 1]; +	ffi_cif cif; +	int status; + +	log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str()); + +	log_assert(SIZE(args) == SIZE(argtypes)); +	for (int i = 0; i < SIZE(args); i++) { +		if (argtypes[i] == "real") { +			log("  arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); +			value_store[i].f64 = args[i]->asReal(args[i]->is_signed); +			values[i] = &value_store[i].f64; +			types[i] = &ffi_type_double; +		} else if (argtypes[i] == "shortreal") { +			log("  arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); +			value_store[i].f32 = args[i]->asReal(args[i]->is_signed); +			values[i] = &value_store[i].f32; +			types[i] = &ffi_type_double; +		} else if (argtypes[i] == "integer") { +			log("  arg %d (%s): %lld\n", i, argtypes[i].c_str(), (long long)args[i]->asInt(args[i]->is_signed)); +			value_store[i].i32 = args[i]->asInt(args[i]->is_signed); +			values[i] = &value_store[i].i32; +			types[i] = &ffi_type_sint32; +		} else { +			log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i); +		} +	} + +        if (rtype == "integer") { +                types[args.size()] = &ffi_type_slong; +                values[args.size()] = &value_store[args.size()].i32; +        } else if (rtype == "shortreal") { +                types[args.size()] = &ffi_type_float; +                values[args.size()] = &value_store[args.size()].f32; +        } else if (rtype == "real") { +                types[args.size()] = &ffi_type_double; +                values[args.size()] = &value_store[args.size()].f64; +        } else { +                log_error("invalid rtype '%s'.\n", rtype.c_str()); +        } + +        if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK) +                log_error("ffi_prep_cif failed: status %d.\n", status); + +        ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values); + +	if (rtype == "real") { +		newNode = new AstNode(AST_REALVALUE); +		newNode->realvalue = value_store[args.size()].f64; +		log("  return realvalue: %g\n", newNode->asReal(true)); +	} else if (rtype == "shortreal") { +		newNode = new AstNode(AST_REALVALUE); +		newNode->realvalue = value_store[args.size()].f32; +		log("  return realvalue: %g\n", newNode->asReal(true)); +	} else { +		newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); +		log("  return integer: %lld\n", (long long)newNode->asInt(true)); +	} + +	return newNode; +} + +#else /* YOSYS_ENABLE_PLUGINS */ + +AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector<std::string>&, const std::vector<AstNode*>&) +{ +	log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str()); +} + +#endif /* YOSYS_ENABLE_PLUGINS */ + diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 591d027cb..f87a68f67 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -27,14 +27,16 @@   */  #include "kernel/log.h" +#include "kernel/utils.h"  #include "libs/sha1/sha1.h"  #include "ast.h"  #include <sstream>  #include <stdarg.h> -#include <assert.h>  #include <algorithm> +YOSYS_NAMESPACE_BEGIN +  using namespace AST;  using namespace AST_INTERNAL; @@ -42,28 +44,13 @@ using namespace AST_INTERNAL;  static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true)  {  	std::stringstream sstr; -	sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); +	sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++); -	RTLIL::Cell *cell = new RTLIL::Cell; +	RTLIL::Cell *cell = current_module->addCell(sstr.str(), type);  	cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	cell->name = sstr.str(); -	cell->type = type; -	current_module->cells[cell->name] = cell; -	RTLIL::Wire *wire = new RTLIL::Wire; +	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);  	wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	wire->name = cell->name + "_Y"; -	wire->width = result_width; -	current_module->wires[wire->name] = wire; - -	RTLIL::SigChunk chunk; -	chunk.wire = wire; -	chunk.width = wire->width; -	chunk.offset = 0; - -	RTLIL::SigSpec sig; -	sig.chunks.push_back(chunk); -	sig.width = chunk.width;  	if (gen_attributes)  		for (auto &attr : that->attributes) { @@ -74,45 +61,30 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi  		}  	cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed); -	cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.width); -	cell->connections["\\A"] = arg; +	cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.size()); +	cell->setPort("\\A", arg);  	cell->parameters["\\Y_WIDTH"] = result_width; -	cell->connections["\\Y"] = sig; -	return sig; +	cell->setPort("\\Y", wire); +	return wire;  }  // helper function for extending bit width (preferred over SigSpec::extend() because of correct undef propagation in ConstEval)  static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed)  { -	if (width <= sig.width) { +	if (width <= sig.size()) {  		sig.extend(width, is_signed);  		return;  	}  	std::stringstream sstr; -	sstr << "$extend" << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); +	sstr << "$extend" << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++); -	RTLIL::Cell *cell = new RTLIL::Cell; +	RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$pos");  	cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	cell->name = sstr.str(); -	cell->type = "$pos"; -	current_module->cells[cell->name] = cell; -	RTLIL::Wire *wire = new RTLIL::Wire; +	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width);  	wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	wire->name = cell->name + "_Y"; -	wire->width = width; -	current_module->wires[wire->name] = wire; - -	RTLIL::SigChunk chunk; -	chunk.wire = wire; -	chunk.width = wire->width; -	chunk.offset = 0; - -	RTLIL::SigSpec new_sig; -	new_sig.chunks.push_back(chunk); -	new_sig.width = chunk.width;  	if (that != NULL)  		for (auto &attr : that->attributes) { @@ -123,40 +95,25 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s  		}  	cell->parameters["\\A_SIGNED"] = RTLIL::Const(is_signed); -	cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.width); -	cell->connections["\\A"] = sig; +	cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.size()); +	cell->setPort("\\A", sig);  	cell->parameters["\\Y_WIDTH"] = width; -	cell->connections["\\Y"] = new_sig; -	sig = new_sig; +	cell->setPort("\\Y", wire); +	sig = wire;  }  // helper function for creating RTLIL code for binary operations  static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)  {  	std::stringstream sstr; -	sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); +	sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++); -	RTLIL::Cell *cell = new RTLIL::Cell; +	RTLIL::Cell *cell = current_module->addCell(sstr.str(), type);  	cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	cell->name = sstr.str(); -	cell->type = type; -	current_module->cells[cell->name] = cell; -	RTLIL::Wire *wire = new RTLIL::Wire; +	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);  	wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	wire->name = cell->name + "_Y"; -	wire->width = result_width; -	current_module->wires[wire->name] = wire; - -	RTLIL::SigChunk chunk; -	chunk.wire = wire; -	chunk.width = wire->width; -	chunk.offset = 0; - -	RTLIL::SigSpec sig; -	sig.chunks.push_back(chunk); -	sig.width = chunk.width;  	for (auto &attr : that->attributes) {  		if (attr.second->type != AST_CONSTANT) @@ -168,45 +125,30 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi  	cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed);  	cell->parameters["\\B_SIGNED"] = RTLIL::Const(that->children[1]->is_signed); -	cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.width); -	cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.width); +	cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.size()); +	cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.size()); -	cell->connections["\\A"] = left; -	cell->connections["\\B"] = right; +	cell->setPort("\\A", left); +	cell->setPort("\\B", right);  	cell->parameters["\\Y_WIDTH"] = result_width; -	cell->connections["\\Y"] = sig; -	return sig; +	cell->setPort("\\Y", wire); +	return wire;  }  // helper function for creating RTLIL code for multiplexers  static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)  { -	assert(cond.width == 1); +	log_assert(cond.size() == 1);  	std::stringstream sstr; -	sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); +	sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (autoidx++); -	RTLIL::Cell *cell = new RTLIL::Cell; +	RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$mux");  	cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	cell->name = sstr.str(); -	cell->type = "$mux"; -	current_module->cells[cell->name] = cell; -	RTLIL::Wire *wire = new RTLIL::Wire; +	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size());  	wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); -	wire->name = cell->name + "_Y"; -	wire->width = left.width; -	current_module->wires[wire->name] = wire; - -	RTLIL::SigChunk chunk; -	chunk.wire = wire; -	chunk.width = wire->width; -	chunk.offset = 0; - -	RTLIL::SigSpec sig; -	sig.chunks.push_back(chunk); -	sig.width = chunk.width;  	for (auto &attr : that->attributes) {  		if (attr.second->type != AST_CONSTANT) @@ -215,14 +157,14 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const  		cell->attributes[attr.first] = attr.second->asAttrConst();  	} -	cell->parameters["\\WIDTH"] = RTLIL::Const(left.width); +	cell->parameters["\\WIDTH"] = RTLIL::Const(left.size()); -	cell->connections["\\A"] = right; -	cell->connections["\\B"] = left; -	cell->connections["\\S"] = cond; -	cell->connections["\\Y"] = sig; +	cell->setPort("\\A", right); +	cell->setPort("\\B", left); +	cell->setPort("\\S", cond); +	cell->setPort("\\Y", wire); -	return sig; +	return wire;  }  // helper class for converting AST always nodes to RTLIL processes @@ -232,23 +174,23 @@ struct AST_INTERNAL::ProcessGenerator  	AstNode *always;  	RTLIL::SigSpec initSyncSignals;  	RTLIL::Process *proc; -	const RTLIL::SigSpec &outputSignals; +	RTLIL::SigSpec outputSignals;  	// This always points to the RTLIL::CaseRule beeing filled at the moment  	RTLIL::CaseRule *current_case; -	// This two variables contain the replacement pattern to be used in the right hand side +	// This map contains the replacement pattern to be used in the right hand side  	// of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right  	// hand side of the 2nd assignment needs to be replace with the temporary signal holding  	// the value assigned in the first assignment. So when the first assignement is processed  	// the according information is appended to subst_rvalue_from and subst_rvalue_to. -	RTLIL::SigSpec subst_rvalue_from, subst_rvalue_to; +	stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_rvalue_map; -	// This two variables contain the replacement pattern to be used in the left hand side +	// This map contains the replacement pattern to be used in the left hand side  	// of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar  	// should not be connected to the signal foo. Instead it must be connected to the temporary  	// signal that is used as input for the register that drives the signal foo. -	RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to; +	stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_lvalue_map;  	// The code here generates a number of temprorary signal for each output register. This  	// map helps generating nice numbered names for all this temporary signals. @@ -257,12 +199,12 @@ struct AST_INTERNAL::ProcessGenerator  	// Buffer for generating the init action  	RTLIL::SigSpec init_lvalue, init_rvalue; -	ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), outputSignals(subst_lvalue_from) +	ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg)  	{  		// generate process and simple root case  		proc = new RTLIL::Process;  		proc->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum); -		proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, RTLIL::autoidx++); +		proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, autoidx++);  		for (auto &attr : always->attributes) {  			if (attr.second->type != AST_CONSTANT)  				log_error("Attribute `%s' with non-constant value at %s:%d!\n", @@ -273,8 +215,10 @@ struct AST_INTERNAL::ProcessGenerator  		current_case = &proc->root_case;  		// create initial temporary signal for all output registers +		RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to;  		collect_lvalues(subst_lvalue_from, always, true, true);  		subst_lvalue_to = new_temp_signal(subst_lvalue_from); +		subst_lvalue_map = subst_lvalue_from.to_sigbit_map(subst_lvalue_to);  		bool found_anyedge_syncs = false;  		for (auto child : always->children) @@ -310,8 +254,7 @@ struct AST_INTERNAL::ProcessGenerator  		// create initial assignments for the temporary signals  		if ((flag_nolatches || always->get_bool_attribute("\\nolatches") || current_module->get_bool_attribute("\\nolatches")) && !found_clocked_sync) { -			subst_rvalue_from = subst_lvalue_from; -			subst_rvalue_to = RTLIL::SigSpec(RTLIL::State::Sx, subst_rvalue_from.width); +			subst_rvalue_map = subst_lvalue_from.to_sigbit_map(RTLIL::SigSpec(RTLIL::State::Sx, SIZE(subst_lvalue_from)));  		} else {  			addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);  		} @@ -321,51 +264,70 @@ struct AST_INTERNAL::ProcessGenerator  			if (child->type == AST_BLOCK)  				processAst(child); -		if (initSyncSignals.width > 0) +		if (initSyncSignals.size() > 0)  		{  			RTLIL::SyncRule *sync = new RTLIL::SyncRule;  			sync->type = RTLIL::SyncType::STi;  			proc->syncs.push_back(sync); -			assert(init_lvalue.width == init_rvalue.width); -			init_lvalue.optimize(); -			init_rvalue.optimize(); +			log_assert(init_lvalue.size() == init_rvalue.size());  			int offset = 0; -			for (size_t i = 0; i < init_lvalue.chunks.size(); i++) { -				RTLIL::SigSpec lhs = init_lvalue.chunks[i]; -				RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue.chunks[i].width); +			for (auto &init_lvalue_c : init_lvalue.chunks()) { +				RTLIL::SigSpec lhs = init_lvalue_c; +				RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width); +				remove_unwanted_lvalue_bits(lhs, rhs);  				sync->actions.push_back(RTLIL::SigSig(lhs, rhs)); -				offset += lhs.width; +				offset += lhs.size();  			}  		} + +		outputSignals = RTLIL::SigSpec(subst_lvalue_from); +	} + +	void remove_unwanted_lvalue_bits(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) +	{ +		RTLIL::SigSpec new_lhs, new_rhs; + +		log_assert(SIZE(lhs) == SIZE(rhs)); +		for (int i = 0; i < SIZE(lhs); i++) { +			if (lhs[i].wire == nullptr) +				continue; +			new_lhs.append(lhs[i]); +			new_rhs.append(rhs[i]); +		} + +		lhs = new_lhs; +		rhs = new_rhs;  	}  	// create new temporary signals  	RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig)  	{ -		sig.optimize(); -		for (size_t i = 0; i < sig.chunks.size(); i++) +		std::vector<RTLIL::SigChunk> chunks = sig.chunks(); + +		for (int i = 0; i < SIZE(chunks); i++)  		{ -			RTLIL::SigChunk &chunk = sig.chunks[i]; +			RTLIL::SigChunk &chunk = chunks[i];  			if (chunk.wire == NULL)  				continue; -			RTLIL::Wire *wire = new RTLIL::Wire; -			wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum); +			std::string wire_name;  			do { -				wire->name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++, +				wire_name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++,  						chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);; -				if (chunk.wire->name.find('$') != std::string::npos) -					wire->name += stringf("$%d", RTLIL::autoidx++); -			} while (current_module->wires.count(wire->name) > 0); -			wire->width = chunk.width; -			current_module->wires[wire->name] = wire; +				if (chunk.wire->name.str().find('$') != std::string::npos) +					wire_name += stringf("$%d", autoidx++); +			} while (current_module->wires_.count(wire_name) > 0); + +			RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); +			wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum);  			chunk.wire = wire;  			chunk.offset = 0;  		} -		return sig; + +		return chunks;  	}  	// recursively traverse the AST an collect all assigned signals @@ -376,7 +338,7 @@ struct AST_INTERNAL::ProcessGenerator  		case AST_CASE:  			for (auto child : ast->children)  				if (child != ast->children[0]) { -					assert(child->type == AST_COND); +					log_assert(child->type == AST_COND);  					collect_lvalues(reg, child, type_eq, type_le, false);  				}  			break; @@ -401,18 +363,23 @@ struct AST_INTERNAL::ProcessGenerator  			break;  		default: -			assert(0); +			log_abort();  		} -		if (run_sort_and_unify) -			reg.sort_and_unify(); +		if (run_sort_and_unify) { +			std::set<RTLIL::SigBit> sorted_reg; +			for (auto bit : reg) +				if (bit.wire) +					sorted_reg.insert(bit); +			reg = RTLIL::SigSpec(sorted_reg); +		}  	}  	// remove all assignments to the given signal pattern in a case and all its children.  	// e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this  	// function is called to clean up the first two assignments as they are overwritten by  	// the third assignment. -	void removeSignalFromCaseTree(RTLIL::SigSpec pattern, RTLIL::CaseRule *cs) +	void removeSignalFromCaseTree(const std::set<RTLIL::SigBit> &pattern, RTLIL::CaseRule *cs)  	{  		for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)  			it->first.remove2(pattern, &it->second); @@ -426,23 +393,22 @@ struct AST_INTERNAL::ProcessGenerator  	// are avoided and the generated $mux cells have a more "natural" size.  	void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false)  	{ -		if (inSyncRule && initSyncSignals.width > 0) { +		if (inSyncRule && initSyncSignals.size() > 0) {  			init_lvalue.append(lvalue.extract(initSyncSignals));  			init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue));  			lvalue.remove2(initSyncSignals, &rvalue);  		} -		assert(lvalue.width == rvalue.width); -		lvalue.optimize(); -		rvalue.optimize(); +		log_assert(lvalue.size() == rvalue.size());  		int offset = 0; -		for (size_t i = 0; i < lvalue.chunks.size(); i++) { -			RTLIL::SigSpec lhs = lvalue.chunks[i]; -			RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue.chunks[i].width); -			if (inSyncRule && lvalue.chunks[i].wire && lvalue.chunks[i].wire->get_bool_attribute("\\nosync")) -				rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.width); +		for (auto &lvalue_c : lvalue.chunks()) { +			RTLIL::SigSpec lhs = lvalue_c; +			RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue_c.width); +			if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute("\\nosync")) +				rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size()); +			remove_unwanted_lvalue_bits(lhs, rhs);  			actions.push_back(RTLIL::SigSig(lhs, rhs)); -			offset += lhs.width; +			offset += lhs.size();  		}  	} @@ -460,18 +426,16 @@ struct AST_INTERNAL::ProcessGenerator  		case AST_ASSIGN_LE:  			{  				RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; -				RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.width, &subst_rvalue_from, &subst_rvalue_to); -				lvalue.replace(subst_lvalue_from, subst_lvalue_to); +				RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap()); +				lvalue.replace(subst_lvalue_map.stdmap());  				if (ast->type == AST_ASSIGN_EQ) { -					subst_rvalue_from.remove2(unmapped_lvalue, &subst_rvalue_to); -					subst_rvalue_from.append(unmapped_lvalue); -					subst_rvalue_from.optimize(); -					subst_rvalue_to.append(rvalue); -					subst_rvalue_to.optimize(); +					for (int i = 0; i < SIZE(unmapped_lvalue); i++) +						subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]);  				} -				removeSignalFromCaseTree(lvalue, current_case); +				removeSignalFromCaseTree(lvalue.to_sigbit_set(), current_case); +				remove_unwanted_lvalue_bits(lvalue, rvalue);  				current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));  			}  			break; @@ -479,7 +443,7 @@ struct AST_INTERNAL::ProcessGenerator  		case AST_CASE:  			{  				RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; -				sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_from, &subst_rvalue_to); +				sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());  				current_case->switches.push_back(sw);  				for (auto &attr : ast->attributes) { @@ -495,13 +459,7 @@ struct AST_INTERNAL::ProcessGenerator  				RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue);  				RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue; -				this_case_eq_rvalue.replace(subst_rvalue_from, subst_rvalue_to); - -				RTLIL::SigSpec backup_subst_lvalue_from = subst_lvalue_from; -				RTLIL::SigSpec backup_subst_lvalue_to = subst_lvalue_to; - -				RTLIL::SigSpec backup_subst_rvalue_from = subst_rvalue_from; -				RTLIL::SigSpec backup_subst_rvalue_to = subst_rvalue_to; +				this_case_eq_rvalue.replace(subst_rvalue_map.stdmap());  				RTLIL::CaseRule *default_case = NULL;  				RTLIL::CaseRule *last_generated_case = NULL; @@ -509,19 +467,13 @@ struct AST_INTERNAL::ProcessGenerator  				{  					if (child == ast->children[0])  						continue; -					assert(child->type == AST_COND); - -					subst_lvalue_from = backup_subst_lvalue_from; -					subst_lvalue_to = backup_subst_lvalue_to; +					log_assert(child->type == AST_COND); -					subst_rvalue_from = backup_subst_rvalue_from; -					subst_rvalue_to = backup_subst_rvalue_to; +					subst_lvalue_map.save(); +					subst_rvalue_map.save(); -					subst_lvalue_from.remove2(this_case_eq_lvalue, &subst_lvalue_to); -					subst_lvalue_from.append(this_case_eq_lvalue); -					subst_lvalue_from.optimize(); -					subst_lvalue_to.append(this_case_eq_ltemp); -					subst_lvalue_to.optimize(); +					for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) +						subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);  					RTLIL::CaseRule *backup_case = current_case;  					current_case = new RTLIL::CaseRule; @@ -533,13 +485,16 @@ struct AST_INTERNAL::ProcessGenerator  						else if (node->type == AST_BLOCK)  							processAst(node);  						else -							current_case->compare.push_back(node->genWidthRTLIL(sw->signal.width, &subst_rvalue_from, &subst_rvalue_to)); +							current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap()));  					}  					if (default_case != current_case)  						sw->cases.push_back(current_case);  					else  						log_assert(current_case->compare.size() == 0);  					current_case = backup_case; + +					subst_lvalue_map.restore(); +					subst_rvalue_map.restore();  				}  				if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) { @@ -552,20 +507,11 @@ struct AST_INTERNAL::ProcessGenerator  					sw->cases.push_back(default_case);  				} -				subst_lvalue_from = backup_subst_lvalue_from; -				subst_lvalue_to = backup_subst_lvalue_to; - -				subst_rvalue_from = backup_subst_rvalue_from; -				subst_rvalue_to = backup_subst_rvalue_to; +				for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) +					subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); -				subst_rvalue_from.remove2(this_case_eq_lvalue, &subst_rvalue_to); -				subst_rvalue_from.append(this_case_eq_lvalue); -				subst_rvalue_from.optimize(); -				subst_rvalue_to.append(this_case_eq_ltemp); -				subst_rvalue_to.optimize(); - -				this_case_eq_lvalue.replace(subst_lvalue_from, subst_lvalue_to); -				removeSignalFromCaseTree(this_case_eq_lvalue, current_case); +				this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); +				removeSignalFromCaseTree(this_case_eq_lvalue.to_sigbit_set(), current_case);  				addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);  			}  			break; @@ -579,13 +525,13 @@ struct AST_INTERNAL::ProcessGenerator  			break;  		default: -			assert(0); +			log_abort();  		}  	}  };  // detect sign and width of an expression -void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) +void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real)  {  	std::string type_name;  	bool sub_sign_hint = true; @@ -594,6 +540,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  	AstNode *range = NULL;  	AstNode *id_ast = NULL; +	bool local_found_real = false; +	if (found_real == NULL) +		found_real = &local_found_real; +  	switch (type)  	{  	case AST_CONSTANT: @@ -602,6 +552,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  			sign_hint = false;  		break; +	case AST_REALVALUE: +		*found_real = true; +		width_hint = std::max(width_hint, 32); +		break; +  	case AST_IDENTIFIER:  		id_ast = id2ast;  		if (id_ast == NULL && current_scope.count(str)) @@ -623,10 +578,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  				if (id_ast->type == AST_AUTOWIRE)  					this_width = 1;  				else { -					// current_ast_mod->dumpAst(stdout, ""); -					// printf("---\n"); -					// dumpAst(stdout, ""); -					// fflush(stdout); +					// current_ast_mod->dumpAst(NULL, "mod> "); +					// log("---\n"); +					// id_ast->dumpAst(NULL, "decl> "); +					// dumpAst(NULL, "ref> ");  					log_error("Failed to detect with of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);  				}  			} else { @@ -648,8 +603,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  			else if (!range->range_valid) {  				AstNode *left_at_zero_ast = children[0]->children[0]->clone();  				AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); -				while (left_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } -				while (right_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } +				while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } +				while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }  				if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)  					log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",  							str.c_str(), filename.c_str(), linenum); @@ -658,14 +613,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  				delete right_at_zero_ast;  			} else  				this_width = range->range_left - range->range_right + 1; -		} else -			width_hint = std::max(width_hint, this_width); +			sign_hint = false; +		} +		width_hint = std::max(width_hint, this_width);  		if (!id_ast->is_signed)  			sign_hint = false;  		break;  	case AST_TO_BITS: -		while (children[0]->simplify(true, false, false, 1, -1, false) == true) { } +		while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { }  		if (children[0]->type != AST_CONSTANT)  			log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum);  		children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); @@ -693,7 +649,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  		break;  	case AST_REPLICATE: -		while (children[0]->simplify(true, false, false, 1, -1, false) == true) { } +		while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }  		if (children[0]->type != AST_CONSTANT)  			log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);  		children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); @@ -704,7 +660,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  	case AST_NEG:  	case AST_BIT_NOT:  	case AST_POS: -		children[0]->detectSignWidthWorker(width_hint, sign_hint); +		children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real);  		break;  	case AST_BIT_AND: @@ -712,7 +668,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  	case AST_BIT_XOR:  	case AST_BIT_XNOR:  		for (auto child : children) -			child->detectSignWidthWorker(width_hint, sign_hint); +			child->detectSignWidthWorker(width_hint, sign_hint, found_real);  		break;  	case AST_REDUCE_AND: @@ -729,7 +685,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  	case AST_SHIFT_SLEFT:  	case AST_SHIFT_SRIGHT:  	case AST_POW: -		children[0]->detectSignWidthWorker(width_hint, sign_hint); +		children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real);  		break;  	case AST_LT: @@ -750,7 +706,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  	case AST_DIV:  	case AST_MOD:  		for (auto child : children) -			child->detectSignWidthWorker(width_hint, sign_hint); +			child->detectSignWidthWorker(width_hint, sign_hint, found_real);  		break;  	case AST_LOGIC_AND: @@ -761,8 +717,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  		break;  	case AST_TERNARY: -		children.at(1)->detectSignWidthWorker(width_hint, sign_hint); -		children.at(2)->detectSignWidthWorker(width_hint, sign_hint); +		children.at(1)->detectSignWidthWorker(width_hint, sign_hint, found_real); +		children.at(2)->detectSignWidthWorker(width_hint, sign_hint, found_real);  		break;  	case AST_MEMRD: @@ -781,13 +737,19 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint)  		log_error("Don't know how to detect sign and width for %s node at %s:%d!\n",  				type2str(type).c_str(), filename.c_str(), linenum);  	} + +	if (*found_real) +		sign_hint = true;  }  // detect sign and width of an expression -void AstNode::detectSignWidth(int &width_hint, bool &sign_hint) +void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real)  { -	width_hint = -1, sign_hint = true; -	detectSignWidthWorker(width_hint, sign_hint); +	width_hint = -1; +	sign_hint = true; +	if (found_real) +		*found_real = false; +	detectSignWidthWorker(width_hint, sign_hint, found_real);  }  // create RTLIL from an AST node @@ -815,6 +777,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	// and are only accessed here thru this references  	case AST_TASK:  	case AST_FUNCTION: +	case AST_DPI_FUNCTION:  	case AST_AUTOWIRE:  	case AST_LOCALPARAM:  	case AST_DEFPARAM: @@ -832,28 +795,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	// create an RTLIL::Wire for an AST_WIRE node  	case AST_WIRE: { -			if (current_module->wires.count(str) != 0) +			if (current_module->wires_.count(str) != 0)  				log_error("Re-definition of signal `%s' at %s:%d!\n",  						str.c_str(), filename.c_str(), linenum);  			if (!range_valid)  				log_error("Signal `%s' with non-constant width at %s:%d!\n",  						str.c_str(), filename.c_str(), linenum); -			if (range_left < range_right && (range_left != -1 || range_right != 0)) { -				int tmp = range_left; -				range_left = range_right; -				range_right = tmp; -			} +			log_assert(range_left >= range_right || (range_left == -1 && range_right == 0)); -			RTLIL::Wire *wire = new RTLIL::Wire; +			RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1);  			wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); -			wire->name = str; -			wire->width = range_left - range_right + 1;  			wire->start_offset = range_right;  			wire->port_id = port_id;  			wire->port_input = is_input;  			wire->port_output = is_output; -			current_module->wires[wire->name] = wire; +			wire->upto = range_swapped;  			for (auto &attr : attributes) {  				if (attr.second->type != AST_CONSTANT) @@ -870,9 +827,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				log_error("Re-definition of memory `%s' at %s:%d!\n",  						str.c_str(), filename.c_str(), linenum); -			assert(children.size() >= 2); -			assert(children[0]->type == AST_RANGE); -			assert(children[1]->type == AST_RANGE); +			log_assert(children.size() >= 2); +			log_assert(children[0]->type == AST_RANGE); +			log_assert(children[1]->type == AST_RANGE);  			if (!children[0]->range_valid || !children[1]->range_valid)  				log_error("Memory `%s' with non-constant width or size at %s:%d!\n", @@ -905,17 +862,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			if (width_hint < 0)  				detectSignWidth(width_hint, sign_hint); -			RTLIL::SigChunk chunk; -			chunk.wire = NULL; -			chunk.data.bits = bits; -			chunk.width = bits.size(); -			chunk.offset = 0; - -			RTLIL::SigSpec sig; -			sig.chunks.push_back(chunk); -			sig.width = chunk.width; -  			is_signed = sign_hint; +			return RTLIL::SigSpec(bitsAsConst()); +		} + +	case AST_REALVALUE: +		{ +			RTLIL::SigSpec sig = realAsConst(width_hint); +			log("Warning: converting real value %e to binary %s at %s:%d.\n", +					realvalue, log_signal(sig), filename.c_str(), linenum);  			return sig;  		} @@ -927,12 +882,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			RTLIL::Wire *wire = NULL;  			RTLIL::SigChunk chunk; -			if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires.count(str) == 0) { -				RTLIL::Wire *wire = new RTLIL::Wire; +			int add_undef_bits_msb = 0; +			int add_undef_bits_lsb = 0; + +			if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { +				RTLIL::Wire *wire = current_module->addWire(str);  				wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);  				wire->name = str; -				log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); -				current_module->wires[str] = wire; +				if (flag_autowire) +					log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); +				else +					log_error("Identifier `%s' is implicitly declared at %s:%d and `default_nettype is set to none.\n", str.c_str(), filename.c_str(), linenum);  			}  			else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {  				if (id2ast->children[0]->type != AST_CONSTANT) @@ -942,7 +902,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				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) +					id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)  				log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n",  						str.c_str(), filename.c_str(), linenum); @@ -950,19 +910,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n",  						str.c_str(), filename.c_str(), linenum); -			wire = current_module->wires[str]; +			wire = current_module->wires_[str];  			chunk.wire = wire;  			chunk.width = wire->width;  			chunk.offset = 0;  		use_const_chunk:  			if (children.size() != 0) { -				assert(children[0]->type == AST_RANGE); +				log_assert(children[0]->type == AST_RANGE); +				int source_width = id2ast->range_left - id2ast->range_right + 1; +				int source_offset = id2ast->range_right;  				if (!children[0]->range_valid) {  					AstNode *left_at_zero_ast = children[0]->children[0]->clone();  					AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); -					while (left_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } -					while (right_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } +					while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } +					while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }  					if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)  						log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",  								str.c_str(), filename.c_str(), linenum); @@ -970,27 +932,59 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  					AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?  							children[0]->children[1]->clone() : children[0]->children[0]->clone());  					fake_ast->children[0]->delete_children(); -					RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shr", width, -							fake_ast->children[0]->genRTLIL(), fake_ast->children[1]->genRTLIL()); +					RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(); +					if (id2ast->range_right != 0) { +						shift_val = current_module->Sub(NEW_ID, shift_val, id2ast->range_right, fake_ast->children[1]->is_signed); +						fake_ast->children[1]->is_signed = true; +					} +					if (id2ast->range_swapped) { +						shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast->children[1]->is_signed); +						fake_ast->children[1]->is_signed = true; +					} +					if (SIZE(shift_val) >= 32) +						fake_ast->children[1]->is_signed = true; +					RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shiftx", width, fake_ast->children[0]->genRTLIL(), shift_val);  					delete left_at_zero_ast;  					delete right_at_zero_ast;  					delete fake_ast;  					return sig;  				} else { -					chunk.offset = children[0]->range_right - id2ast->range_right;  					chunk.width = children[0]->range_left - children[0]->range_right + 1; -					if (children[0]->range_left > id2ast->range_left || id2ast->range_right > children[0]->range_right) -						log_error("Range select out of bounds on signal `%s' at %s:%d!\n", -								str.c_str(), filename.c_str(), linenum); +					chunk.offset = children[0]->range_right - source_offset; +					if (id2ast->range_swapped) +						chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width); +					if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) { +						if (chunk.width == 1) +							log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n", +									str.c_str(), filename.c_str(), linenum); +						else +							log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n", +									str.c_str(), filename.c_str(), linenum, chunk.width); +						chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); +					} else { +						if (chunk.width + chunk.offset > source_width) { +							add_undef_bits_msb = (chunk.width + chunk.offset) - source_width; +							chunk.width -= add_undef_bits_msb; +						} +						if (chunk.offset < 0) { +							add_undef_bits_lsb = -chunk.offset; +							chunk.width -= add_undef_bits_lsb; +							chunk.offset += add_undef_bits_lsb; +						} +						if (add_undef_bits_lsb) +							log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n", +									str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb); +						if (add_undef_bits_msb) +							log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n", +									str.c_str(), filename.c_str(), linenum, add_undef_bits_msb); +					}  				}  			} -			RTLIL::SigSpec sig; -			sig.chunks.push_back(chunk); -			sig.width = chunk.width; +			RTLIL::SigSpec sig = { RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_msb), chunk, RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_lsb) }; -			if (genRTLIL_subst_from && genRTLIL_subst_to) -				sig.replace(*genRTLIL_subst_from, *genRTLIL_subst_to); +			if (genRTLIL_subst_ptr) +				sig.replace(*genRTLIL_subst_ptr);  			is_signed = children.size() > 0 ? false : id2ast->is_signed && sign_hint;  			return sig; @@ -1000,7 +994,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	case AST_TO_SIGNED:  	case AST_TO_UNSIGNED: {  			RTLIL::SigSpec sig = children[0]->genRTLIL(); -			if (sig.width < width_hint) +			if (sig.size() < width_hint)  				sig.extend_u0(width_hint, sign_hint);  			is_signed = sign_hint;  			return sig; @@ -1009,15 +1003,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	// concatenation of signals can be done directly using RTLIL::SigSpec  	case AST_CONCAT: {  			RTLIL::SigSpec sig; -			sig.width = 0; -			for (auto it = children.begin(); it != children.end(); it++) { -				RTLIL::SigSpec s = (*it)->genRTLIL(); -				for (size_t i = 0; i < s.chunks.size(); i++) { -					sig.chunks.push_back(s.chunks[i]); -					sig.width += s.chunks[i].width; -				} -			} -			if (sig.width < width_hint) +			for (auto it = children.begin(); it != children.end(); it++) +				sig.append((*it)->genRTLIL()); +			if (sig.size() < width_hint)  				sig.extend_u0(width_hint, false);  			return sig;  		} @@ -1032,7 +1020,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			RTLIL::SigSpec sig;  			for (int i = 0; i < count; i++)  				sig.append(right); -			if (sig.width < width_hint) +			if (sig.size() < width_hint)  				sig.extend_u0(width_hint, false);  			is_signed = false;  			return sig; @@ -1045,7 +1033,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  		{  			RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint, sign_hint);  			is_signed = children[0]->is_signed; -			int width = arg.width; +			int width = arg.size();  			if (width_hint > 0) {  				width = width_hint;  				widthExtend(this, arg, width, is_signed); @@ -1063,7 +1051,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				detectSignWidth(width_hint, sign_hint);  			RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);  			RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); -			int width = std::max(left.width, right.width); +			int width = std::max(left.size(), right.size());  			if (width_hint > 0)  				width = width_hint;  			is_signed = children[0]->is_signed && children[1]->is_signed; @@ -1086,7 +1074,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	if (0) { case AST_REDUCE_BOOL:  type_name = "$reduce_bool"; }  		{  			RTLIL::SigSpec arg = children[0]->genRTLIL(); -			RTLIL::SigSpec sig = arg.width > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg; +			RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg;  			return sig;  		} @@ -1100,7 +1088,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				detectSignWidth(width_hint, sign_hint);  			RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);  			RTLIL::SigSpec right = children[1]->genRTLIL(); -			int width = width_hint > 0 ? width_hint : left.width; +			int width = width_hint > 0 ? width_hint : left.size();  			is_signed = children[0]->is_signed;  			return binop2rtlil(this, type_name, width, left, right);  		} @@ -1115,10 +1103,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				detectSignWidth(width_hint, sign_hint);  			RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);  			RTLIL::SigSpec right = children[1]->genRTLIL(right_width, right_signed); -			int width = width_hint > 0 ? width_hint : left.width; +			int width = width_hint > 0 ? width_hint : left.size();  			is_signed = children[0]->is_signed;  			if (!flag_noopt && left.is_fully_const() && left.as_int() == 2 && !right_signed) -				return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.width), right); +				return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.size()), right);  			return binop2rtlil(this, "$pow", width, left, right);  		} @@ -1154,7 +1142,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);  			RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);  		#if 0 -			int width = std::max(left.width, right.width); +			int width = std::max(left.size(), right.size());  			if (width > width_hint && width_hint > 0)  				width = width_hint;  			if (width < width_hint) { @@ -1163,10 +1151,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed))  					width = width_hint;  				if (type == AST_MUL) -					width = std::min(left.width + right.width, width_hint); +					width = std::min(left.size() + right.size(), width_hint);  			}  		#else -			int width = std::max(std::max(left.width, right.width), width_hint); +			int width = std::max(std::max(left.size(), right.size()), width_hint);  		#endif  			is_signed = children[0]->is_signed && children[1]->is_signed;  			return binop2rtlil(this, type_name, width, left, right); @@ -1198,17 +1186,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			RTLIL::SigSpec val1 = children[1]->genRTLIL(width_hint, sign_hint);  			RTLIL::SigSpec val2 = children[2]->genRTLIL(width_hint, sign_hint); -			if (cond.width > 1) +			if (cond.size() > 1)  				cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false); -			int width = std::max(val1.width, val2.width); +			int width = std::max(val1.size(), val2.size());  			is_signed = children[1]->is_signed && children[2]->is_signed;  			widthExtend(this, val1, width, is_signed);  			widthExtend(this, val2, width, is_signed);  			RTLIL::SigSpec sig = mux2rtlil(this, cond, val1, val2); -			if (sig.width < width_hint) +			if (sig.size() < width_hint)  				sig.extend_u0(width_hint, sign_hint);  			return sig;  		} @@ -1217,27 +1205,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	case AST_MEMRD:  		{  			std::stringstream sstr; -			sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +			sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); -			RTLIL::Cell *cell = new RTLIL::Cell; +			RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memrd");  			cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); -			cell->name = sstr.str(); -			cell->type = "$memrd"; -			current_module->cells[cell->name] = cell; -			RTLIL::Wire *wire = new RTLIL::Wire; +			RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width);  			wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); -			wire->name = cell->name + "_DATA"; -			wire->width = current_module->memories[str]->width; -			current_module->wires[wire->name] = wire;  			int addr_bits = 1;  			while ((1 << addr_bits) < current_module->memories[str]->size)  				addr_bits++; -			cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1); -			cell->connections["\\ADDR"] = children[0]->genWidthRTLIL(addr_bits); -			cell->connections["\\DATA"] = RTLIL::SigSpec(wire); +			cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); +			cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); +			cell->setPort("\\DATA", RTLIL::SigSpec(wire));  			cell->parameters["\\MEMID"] = RTLIL::Const(str);  			cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); @@ -1254,25 +1236,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	case AST_MEMWR:  		{  			std::stringstream sstr; -			sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +			sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); -			RTLIL::Cell *cell = new RTLIL::Cell; +			RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memwr");  			cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); -			cell->name = sstr.str(); -			cell->type = "$memwr"; -			current_module->cells[cell->name] = cell;  			int addr_bits = 1;  			while ((1 << addr_bits) < current_module->memories[str]->size)  				addr_bits++; -			cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1); -			cell->connections["\\ADDR"] = children[0]->genWidthRTLIL(addr_bits); -			cell->connections["\\DATA"] = children[1]->genWidthRTLIL(current_module->memories[str]->width); -			cell->connections["\\EN"] = children[2]->genRTLIL(); - -			if (cell->connections["\\EN"].width > 1) -				cell->connections["\\EN"] = uniop2rtlil(this, "$reduce_bool", 1, cell->connections["\\EN"], false); +			cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); +			cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); +			cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width)); +			cell->setPort("\\EN", children[2]->genRTLIL());  			cell->parameters["\\MEMID"] = RTLIL::Const(str);  			cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); @@ -1281,7 +1257,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0);  			cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); -			cell->parameters["\\PRIORITY"] = RTLIL::Const(RTLIL::autoidx-1); +			cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1);  		}  		break; @@ -1291,19 +1267,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			log_assert(children.size() == 2);  			RTLIL::SigSpec check = children[0]->genRTLIL(); -			log_assert(check.width == 1); +			log_assert(check.size() == 1);  			RTLIL::SigSpec en = children[1]->genRTLIL(); -			log_assert(en.width == 1); +			log_assert(en.size() == 1);  			std::stringstream sstr; -			sstr << "$assert$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +			sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); -			RTLIL::Cell *cell = new RTLIL::Cell; +			RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$assert");  			cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); -			cell->name = sstr.str(); -			cell->type = "$assert"; -			current_module->cells[cell->name] = cell;  			for (auto &attr : attributes) {  				if (attr.second->type != AST_CONSTANT) @@ -1312,8 +1285,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				cell->attributes[attr.first] = attr.second->asAttrConst();  			} -			cell->connections["\\A"] = check; -			cell->connections["\\EN"] = en; +			cell->setPort("\\A", check); +			cell->setPort("\\EN", en);  		}  		break; @@ -1322,12 +1295,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  		{  			if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_AUTOWIRE) {  				RTLIL::SigSpec right = children[1]->genRTLIL(); -				RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.width); -				current_module->connections.push_back(RTLIL::SigSig(left, right)); +				RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.size()); +				current_module->connect(RTLIL::SigSig(left, right));  			} else {  				RTLIL::SigSpec left = children[0]->genRTLIL(); -				RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.width); -				current_module->connections.push_back(RTLIL::SigSig(left, right)); +				RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); +				current_module->connect(RTLIL::SigSig(left, right));  			}  		}  		break; @@ -1336,9 +1309,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	case AST_CELL:  		{  			int port_counter = 0, para_counter = 0; -			RTLIL::Cell *cell = new RTLIL::Cell; + +			if (current_module->count_id(str) != 0) +				log_error("Re-definition of cell `%s' at %s:%d!\n", +						str.c_str(), filename.c_str(), linenum); + +			RTLIL::Cell *cell = current_module->addCell(str, "");  			cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); -			cell->name = str; +  			for (auto it = children.begin(); it != children.end(); it++) {  				AstNode *child = *it;  				if (child->type == AST_CELLTYPE) { @@ -1367,13 +1345,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  					if (child->str.size() == 0) {  						char buf[100];  						snprintf(buf, 100, "$%d", ++port_counter); -						cell->connections[buf] = sig; +						cell->setPort(buf, sig);  					} else { -						cell->connections[child->str] = sig; +						cell->setPort(child->str, sig);  					}  					continue;  				} -				assert(0); +				log_abort();  			}  			for (auto &attr : attributes) {  				if (attr.second->type != AST_CONSTANT) @@ -1381,10 +1359,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  							attr.first.c_str(), filename.c_str(), linenum);  				cell->attributes[attr.first] = attr.second->asAttrConst();  			} -			if (current_module->cells.count(cell->name) != 0) -				log_error("Re-definition of cell `%s' at %s:%d!\n", -						str.c_str(), filename.c_str(), linenum); -			current_module->cells[str] = cell;  		}  		break; @@ -1417,23 +1391,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  // this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or  // signals must be substituted before beeing used as input values (used by ProcessGenerator)  // note that this is using some global variables to communicate this special settings to AstNode::genRTLIL(). -RTLIL::SigSpec AstNode::genWidthRTLIL(int width, RTLIL::SigSpec *subst_from,  RTLIL::SigSpec *subst_to) +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const std::map<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)  { -	RTLIL::SigSpec *backup_subst_from = genRTLIL_subst_from; -	RTLIL::SigSpec *backup_subst_to = genRTLIL_subst_to; +	const std::map<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr; -	if (subst_from) -		genRTLIL_subst_from = subst_from; -	if (subst_to) -		genRTLIL_subst_to = subst_to; +	if (new_subst_ptr) +		genRTLIL_subst_ptr = new_subst_ptr;  	bool sign_hint = true;  	int width_hint = width;  	detectSignWidthWorker(width_hint, sign_hint);  	RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint); -	genRTLIL_subst_from = backup_subst_from; -	genRTLIL_subst_to = backup_subst_to; +	genRTLIL_subst_ptr = backup_subst_ptr;  	if (width >= 0)  		sig.extend_u0(width, is_signed); @@ -1441,3 +1411,5 @@ RTLIL::SigSpec AstNode::genWidthRTLIL(int width, RTLIL::SigSpec *subst_from,  RT  	return sig;  } +YOSYS_NAMESPACE_END + diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index b51079ce5..969cc2302 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -32,7 +32,9 @@  #include <sstream>  #include <stdarg.h> -#include <assert.h> +#include <math.h> + +YOSYS_NAMESPACE_BEGIN  using namespace AST;  using namespace AST_INTERNAL; @@ -43,16 +45,23 @@ using namespace AST_INTERNAL;  //  // this function also does all name resolving and sets the id2ast member of all  // nodes that link to a different node using names and lexical scoping. -bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint) +bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param)  {  	AstNode *newNode = NULL;  	bool did_something = false; +#if 0 +	log("-------------\n"); +	log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", +			int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); +	dumpAst(NULL, "> "); +#endif +  	if (stage == 0)  	{ -		assert(type == AST_MODULE); +		log_assert(type == AST_MODULE); -		while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint)) { } +		while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { }  		if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg"))  		{ @@ -66,7 +75,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			{  				AstNode *mem = it.first;  				uint32_t memflags = it.second; -				assert((memflags & ~0x00ffff00) == 0); +				log_assert((memflags & ~0x00ffff00) == 0);  				if (mem->get_bool_attribute("\\nomem2reg"))  					continue; @@ -83,6 +92,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE))  					goto verbose_activate; +				if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) +					goto verbose_activate; +  				// log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));  				continue; @@ -114,7 +126,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  					reg->is_reg = true;  					reg->is_signed = node->is_signed;  					children.push_back(reg); -					while (reg->simplify(true, false, false, 1, -1, false)) { } +					while (reg->simplify(true, false, false, 1, -1, false, false)) { }  				}  			} @@ -128,7 +140,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			}  		} -		while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint)) { } +		while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { }  		return false;  	} @@ -140,7 +152,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_FUNCTION || type == AST_TASK)  		return false; -	// deactivate all calls non-synthesis system taks +	// deactivate all calls to non-synthesis system taks  	if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$stop" || str == "$finish")) {  		delete_children();  		str = std::string(); @@ -152,6 +164,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))  		const_fold = true; +	// in certain cases a function must be evaluated constant. this is what in_param controls. +	if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) +		in_param = true; +  	std::map<std::string, AstNode*> backup_scope;  	// create name resolution entries for all objects with names @@ -202,12 +218,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  					did_something = true;  					delete node;  					continue; +				wires_are_incompatible: +					if (stage > 1) +						log_error("Incompatible re-declaration of wire %s at %s:%d.\n", node->str.c_str(), filename.c_str(), linenum); +					continue;  				}  				this_wire_scope[node->str] = node;  			} -		wires_are_incompatible:  			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || -					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_CELL) { +					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) {  				backup_scope[node->str] = current_scope[node->str];  				current_scope[node->str] = node;  			} @@ -215,7 +234,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		for (size_t i = 0; i < children.size(); i++) {  			AstNode *node = children[i];  			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE) -				while (node->simplify(true, false, false, 1, -1, false)) { } +				while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) +					did_something = true;  		}  	} @@ -229,6 +249,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	bool detect_width_simple = false;  	bool child_0_is_self_determined = false;  	bool child_1_is_self_determined = false; +	bool child_2_is_self_determined = false;  	bool children_are_self_determined = false;  	bool reset_width_after_children = false; @@ -237,8 +258,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	case AST_ASSIGN_EQ:  	case AST_ASSIGN_LE:  	case AST_ASSIGN: -		while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false) == true) { } -		while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false) == true) { } +		while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false, in_param) == true) +			did_something = true; +		while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param) == true) +			did_something = true;  		children[0]->detectSignWidth(backup_width_hint, backup_sign_hint);  		children[1]->detectSignWidth(width_hint, sign_hint);  		width_hint = std::max(width_hint, backup_width_hint); @@ -247,11 +270,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	case AST_PARAMETER:  	case AST_LOCALPARAM: -		while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false) == true) { } +		while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true) +			did_something = true;  		children[0]->detectSignWidth(width_hint, sign_hint); -		if (children.size() > 1) { -			assert(children[1]->type == AST_RANGE); -			while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false) == true) { } +		if (children.size() > 1 && children[1]->type == AST_RANGE) { +			while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, true) == true) +				did_something = true;  			if (!children[1]->range_valid)  				log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);  			width_hint = std::max(width_hint, children[1]->range_left - children[1]->range_right + 1); @@ -307,7 +331,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		width_hint = -1;  		sign_hint = true;  		for (auto child : children) { -			while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false) == true) { } +			while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true) +				did_something = true;  			child->detectSignWidthWorker(width_hint, sign_hint);  		}  		reset_width_after_children = true; @@ -336,13 +361,27 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	}  	if (detect_width_simple && width_hint < 0) { -		for (auto child : children) -			while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false) == true) { }  		if (type == AST_REPLICATE) -			while (children[0]->simplify(true, false, in_lvalue, stage, -1, false) == true) { } +			while (children[0]->simplify(true, false, in_lvalue, stage, -1, false, true) == true) +				did_something = true; +		for (auto child : children) +			while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true) +				did_something = true;  		detectSignWidth(width_hint, sign_hint);  	} +	if (type == AST_TERNARY) { +		int width_hint_left, width_hint_right; +		bool sign_hint_left, sign_hint_right; +		bool found_real_left, found_real_right; +		children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left); +		children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right); +		if (found_real_left || found_real_right) { +			child_1_is_self_determined = true; +			child_2_is_self_determined = true; +		} +	} +  	// simplify all children first  	// (iterate by index as e.g. auto wires can add new children in the process)  	for (size_t i = 0; i < children.size(); i++) { @@ -361,8 +400,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;  			int width_hint_here = width_hint;  			bool sign_hint_here = sign_hint; -			if (i == 0 && type == AST_REPLICATE) -				const_fold_here = true; +			bool in_param_here = in_param; +			if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE)) +				const_fold_here = true, in_param_here = true;  			if (type == AST_PARAMETER || type == AST_LOCALPARAM)  				const_fold_here = true;  			if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) @@ -377,15 +417,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				width_hint_here = -1, sign_hint_here = false;  			if (i == 1 && child_1_is_self_determined)  				width_hint_here = -1, sign_hint_here = false; +			if (i == 2 && child_2_is_self_determined) +				width_hint_here = -1, sign_hint_here = false;  			if (children_are_self_determined)  				width_hint_here = -1, sign_hint_here = false; -			did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here); +			did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here, in_param_here);  			if (did_something_here)  				did_something = true;  		} +		if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) { +			current_ast_mod->children.push_back(children[i]); +			children.erase(children.begin() + (i--)); +			did_something = true; +		}  	}  	for (auto &attr : attributes) { -		while (attr.second->simplify(true, false, false, stage, -1, false)) { } +		while (attr.second->simplify(true, false, false, stage, -1, false, true)) +			did_something = true;  	}  	if (reset_width_after_children) { @@ -416,11 +464,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_DEFPARAM && !str.empty()) {  		size_t pos = str.rfind('.');  		if (pos == std::string::npos) -			log_error("Defparam `%s' does not contain a dot (module/parameter seperator) at %s:%d!\n", -					RTLIL::id2cstr(str.c_str()), filename.c_str(), linenum); +			log_error("Defparam `%s' does not contain a dot (module/parameter separator) at %s:%d!\n", +					RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);  		std::string modname = str.substr(0, pos), paraname = "\\" + str.substr(pos+1);  		if (current_scope.count(modname) == 0 || current_scope.at(modname)->type != AST_CELL) -			log_error("Can't find cell for defparam `%s . %s` at %s:%d!\n", RTLIL::id2cstr(modname), RTLIL::id2cstr(paraname), filename.c_str(), linenum); +			log_error("Can't find cell for defparam `%s . %s` at %s:%d!\n", RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paraname).c_str(), filename.c_str(), linenum);  		AstNode *cell = current_scope.at(modname), *paraset = clone();  		cell->children.insert(cell->children.begin() + 1, paraset);  		paraset->type = AST_PARASET; @@ -434,7 +482,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			// dumpAst(NULL, ">   ");  			log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum);  		} -		assert(children[1]->type == AST_IDENTIFIER); +		log_assert(children[1]->type == AST_IDENTIFIER);  		newNode = children[1]->clone();  		const char *second_part = children[1]->str.c_str();  		if (second_part[0] == '\\') @@ -458,9 +506,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_RANGE) {  		bool old_range_valid = range_valid;  		range_valid = false; +		range_swapped = false;  		range_left = -1;  		range_right = 0; -		assert(children.size() >= 1); +		log_assert(children.size() >= 1);  		if (children[0]->type == AST_CONSTANT) {  			range_valid = true;  			range_left = children[0]->integer; @@ -479,6 +528,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			int tmp = range_right;  			range_right = range_left;  			range_left = tmp; +			range_swapped = true;  		}  	} @@ -489,6 +539,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				if (!range_valid)  					did_something = true;  				range_valid = true; +				range_swapped = children[0]->range_swapped;  				range_left = children[0]->range_left;  				range_right = children[0]->range_right;  			} @@ -496,24 +547,98 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			if (!range_valid)  				did_something = true;  			range_valid = true; +			range_swapped = false;  			range_left = 0;  			range_right = 0;  		}  	} +	// resolve multiranges on memory decl +	if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE) +	{ +		int total_size = 1; +		multirange_dimensions.clear(); +		for (auto range : children[1]->children) { +			if (!range->range_valid) +				log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum); +			multirange_dimensions.push_back(std::min(range->range_left, range->range_right)); +			multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1); +			total_size *= multirange_dimensions.back(); +		} +		delete children[1]; +		children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true)); +		did_something = true; +	} + +	// resolve multiranges on memory access +	if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE) +	{ +		AstNode *index_expr = nullptr; + +		for (int i = 0; 2*i < SIZE(id2ast->multirange_dimensions); i++) +		{ +			if (SIZE(children[0]->children) < i) +				log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum); + +			AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); + +			if (id2ast->multirange_dimensions[2*i]) +				new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true)); + +			if (i == 0) +				index_expr = new_index_expr; +			else +				index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr); +		} + +		for (int i = SIZE(id2ast->multirange_dimensions)/1; i < SIZE(children[0]->children); i++) +			children.push_back(children[0]->children[i]->clone()); + +		delete children[0]; +		if (index_expr == nullptr) +			children.erase(children.begin()); +		else +			children[0] = new AstNode(AST_RANGE, index_expr); + +		did_something = true; +	} +  	// trim/extend parameters -	if ((type == AST_PARAMETER || type == AST_LOCALPARAM) && children[0]->type == AST_CONSTANT && children.size() > 1) { -		if (!children[1]->range_valid) -			log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); -		int width = children[1]->range_left - children[1]->range_right + 1; -		if (width != int(children[0]->bits.size())) { -			RTLIL::SigSpec sig(children[0]->bits); -			sig.extend_u0(width, children[0]->is_signed); -			AstNode *old_child_0 = children[0]; -			children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed); -			delete old_child_0; +	if (type == AST_PARAMETER || type == AST_LOCALPARAM) { +		if (children.size() > 1 && children[1]->type == AST_RANGE) { +			if (!children[1]->range_valid) +				log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); +			int width = children[1]->range_left - children[1]->range_right + 1; +			if (children[0]->type == AST_REALVALUE) { +				RTLIL::Const constvalue = children[0]->realAsConst(width); +				log("Warning: converting real value %e to binary %s at %s:%d.\n", +						children[0]->realvalue, log_signal(constvalue), filename.c_str(), linenum); +				delete children[0]; +				children[0] = mkconst_bits(constvalue.bits, sign_hint); +				did_something = true; +			} +			if (children[0]->type == AST_CONSTANT) { +				if (width != int(children[0]->bits.size())) { +					RTLIL::SigSpec sig(children[0]->bits); +					sig.extend_u0(width, children[0]->is_signed); +					AstNode *old_child_0 = children[0]; +					children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed); +					delete old_child_0; +				} +				children[0]->is_signed = is_signed; +			} +			range_valid = true; +			range_swapped = children[1]->range_swapped; +			range_left = children[1]->range_left; +			range_right = children[1]->range_right; +		} else +		if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) { +			double as_realvalue = children[0]->asReal(sign_hint); +			delete children[0]; +			children[0] = new AstNode(AST_REALVALUE); +			children[0]->realvalue = as_realvalue; +			did_something = true;  		} -		children[0]->is_signed = is_signed;  	}  	// annotate identifiers using scope resolution and create auto-wires as needed @@ -521,7 +646,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		if (current_scope.count(str) == 0) {  			for (auto node : current_ast_mod->children) {  				if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || -						node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) && str == node->str) { +						node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) {  					current_scope[node->str] = node;  					break;  				} @@ -535,20 +660,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			current_scope[str] = auto_wire;  			did_something = true;  		} -		id2ast = current_scope[str]; +		if (id2ast != current_scope[str]) { +			id2ast = current_scope[str]; +			did_something = true; +		}  	}  	// split memory access with bit select to individual statements -	if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE) +	if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)  	{ -		if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1 || in_lvalue) +		if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)  			log_error("Invalid bit-select on memory access at %s:%d!\n", filename.c_str(), linenum);  		int mem_width, mem_size, addr_bits;  		id2ast->meminfo(mem_width, mem_size, addr_bits);  		std::stringstream sstr; -		sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +		sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);  		std::string wire_id = sstr.str();  		AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); @@ -556,7 +684,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		if (current_block)  			wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);  		current_ast_mod->children.push_back(wire); -		while (wire->simplify(true, false, false, 1, -1, false)) { } +		while (wire->simplify(true, false, false, 1, -1, false, false)) { }  		AstNode *data = clone();  		delete data->children[1]; @@ -587,6 +715,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		goto apply_newNode;  	} +	if (type == AST_WHILE) +		log_error("While loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum); + +	if (type == AST_REPEAT) +		log_error("Repeat loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum); +  	// unroll for loops and generate-for blocks  	if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)  	{ @@ -621,7 +755,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		// eval 1st expression  		AstNode *varbuf = init_ast->children[1]->clone(); -		while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +		while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  		if (varbuf->type != AST_CONSTANT)  			log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -643,7 +777,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		{  			// eval 2nd expression  			AstNode *buf = while_ast->clone(); -			while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +			while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  			if (buf->type != AST_CONSTANT)  				log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -662,7 +796,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				buf = new AstNode(AST_GENBLOCK, body_ast->clone());  			if (buf->str.empty()) {  				std::stringstream sstr; -				sstr << "$genblock$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +				sstr << "$genblock$" << filename << ":" << linenum << "$" << (autoidx++);  				buf->str = sstr.str();  			}  			std::map<std::string, std::string> name_map; @@ -672,7 +806,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			if (type == AST_GENFOR) {  				for (size_t i = 0; i < buf->children.size(); i++) { -					buf->children[i]->simplify(false, false, false, stage, -1, false); +					buf->children[i]->simplify(false, false, false, stage, -1, false, false);  					current_ast_mod->children.push_back(buf->children[i]);  				}  			} else { @@ -684,7 +818,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			// eval 3rd expression  			buf = next_ast->children[1]->clone(); -			while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +			while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  			if (buf->type != AST_CONSTANT)  				log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -708,8 +842,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		std::vector<AstNode*> new_children;  		for (size_t i = 0; i < children.size(); i++)  			if (children[i]->type == AST_WIRE) { -				children[i]->simplify(false, false, false, stage, -1, false); +				children[i]->simplify(false, false, false, stage, -1, false, false);  				current_ast_mod->children.push_back(children[i]); +				current_scope[children[i]->str] = children[i];  			} else  				new_children.push_back(children[i]); @@ -727,7 +862,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		}  		for (size_t i = 0; i < children.size(); i++) { -			children[i]->simplify(false, false, false, stage, -1, false); +			children[i]->simplify(false, false, false, stage, -1, false, false);  			current_ast_mod->children.push_back(children[i]);  		} @@ -739,7 +874,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_GENIF && children.size() != 0)  	{  		AstNode *buf = children[0]->clone(); -		while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +		while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  		if (buf->type != AST_CONSTANT) {  			// for (auto f : log_files)  			// 	dumpAst(f, "verilog-ast> "); @@ -764,7 +899,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			}  			for (size_t i = 0; i < buf->children.size(); i++) { -				buf->children[i]->simplify(false, false, false, stage, -1, false); +				buf->children[i]->simplify(false, false, false, stage, -1, false, false);  				current_ast_mod->children.push_back(buf->children[i]);  			} @@ -780,7 +915,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_GENCASE && children.size() != 0)  	{  		AstNode *buf = children[0]->clone(); -		while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +		while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  		if (buf->type != AST_CONSTANT) {  			// for (auto f : log_files)  			// 	dumpAst(f, "verilog-ast> "); @@ -814,14 +949,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  					continue;  				buf = child->clone(); -				while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +				while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  				if (buf->type != AST_CONSTANT) {  					// for (auto f : log_files)  					// 	dumpAst(f, "verilog-ast> ");  					log_error("Expression in generate case at %s:%d is not constant!\n", filename.c_str(), linenum);  				} -				if (RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool()) { +				bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool(); +				delete buf; + +				if (is_selected) {  					selected_case = this_genblock;  					i = children.size();  					break; @@ -840,7 +978,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			}  			for (size_t i = 0; i < buf->children.size(); i++) { -				buf->children[i]->simplify(false, false, false, stage, -1, false); +				buf->children[i]->simplify(false, false, false, stage, -1, false, false);  				current_ast_mod->children.push_back(buf->children[i]);  			} @@ -852,6 +990,31 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		did_something = true;  	} +	// unroll cell arrays +	if (type == AST_CELLARRAY) +	{ +		if (!children.at(0)->range_valid) +			log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum); + +		newNode = new AstNode(AST_GENBLOCK); +		int num = std::max(children.at(0)->range_left, children.at(0)->range_right) - std::min(children.at(0)->range_left, children.at(0)->range_right) + 1; + +		for (int i = 0; i < num; i++) { +			int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; +			AstNode *new_cell = children.at(1)->clone(); +			newNode->children.push_back(new_cell); +			new_cell->str += stringf("[%d]", idx); +			if (new_cell->type == AST_PRIMITIVE) { +				log_error("Cell arrays of primitives are currently not supported at %s:%d.\n", filename.c_str(), linenum); +			} else { +				log_assert(new_cell->children.at(0)->type == AST_CELLTYPE); +				new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str()); +			} +		} + +		goto apply_newNode; +	} +  	// replace primitives with assignmens  	if (type == AST_PRIMITIVE)  	{ @@ -861,8 +1024,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  		std::vector<AstNode*> children_list;  		for (auto child : children) { -			assert(child->type == AST_ARGUMENT); -			assert(child->children.size() == 1); +			log_assert(child->type == AST_ARGUMENT); +			log_assert(child->children.size() == 1);  			children_list.push_back(child->children[0]);  			child->children.clear();  			delete child; @@ -917,7 +1080,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				op_type = AST_POS;  			if (str == "not")  				op_type = AST_POS, invert_results = true; -			assert(op_type != AST_NONE); +			log_assert(op_type != AST_NONE);  			AstNode *node = children_list[1];  			if (op_type != AST_POS) @@ -955,12 +1118,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			shift_expr = range->children[1]->clone();  			AstNode *left_at_zero_ast = range->children[0]->clone();  			AstNode *right_at_zero_ast = range->children[1]->clone(); -			while (left_at_zero_ast->simplify(true, true, false, stage, -1, false)) { } -			while (right_at_zero_ast->simplify(true, true, false, stage, -1, false)) { } +			while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } +			while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }  			if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)  				log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",  						str.c_str(), filename.c_str(), linenum); -			result_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; +			result_width = abs(left_at_zero_ast->integer - right_at_zero_ast->integer) + 1;  		}  		did_something = true;  		newNode = new AstNode(AST_CASE, shift_expr); @@ -981,14 +1144,14 @@ skip_dynamic_range_lvalue_expansion:;  	if (stage > 1 && type == AST_ASSERT && current_block != NULL)  	{  		std::stringstream sstr; -		sstr << "$assert$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +		sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++);  		std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN";  		AstNode *wire_check = new AstNode(AST_WIRE);  		wire_check->str = id_check;  		current_ast_mod->children.push_back(wire_check);  		current_scope[wire_check->str] = wire_check; -		while (wire_check->simplify(true, false, false, 1, -1, false)) { } +		while (wire_check->simplify(true, false, false, 1, -1, false, false)) { }  		AstNode *wire_en = new AstNode(AST_WIRE);  		wire_en->str = id_en; @@ -996,7 +1159,7 @@ skip_dynamic_range_lvalue_expansion:;  		current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));  		current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;  		current_scope[wire_en->str] = wire_en; -		while (wire_en->simplify(true, false, false, 1, -1, false)) { } +		while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }  		std::vector<RTLIL::State> x_bit;  		x_bit.push_back(RTLIL::State::Sx); @@ -1051,16 +1214,16 @@ skip_dynamic_range_lvalue_expansion:;  	// assignment with memory in left-hand side expression -> replace with memory write port  	if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER && -			children[0]->children.size() == 1 && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && -			children[0]->id2ast->children.size() >= 2 && children[0]->id2ast->children[0]->range_valid && -			children[0]->id2ast->children[1]->range_valid) +			children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 && +			children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid && +			(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)  	{  		std::stringstream sstr; -		sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +		sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);  		std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";  		if (type == AST_ASSIGN_EQ) -			log("Warining: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", +			log("Warning: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",  					filename.c_str(), linenum);  		int mem_width, mem_size, addr_bits; @@ -1070,31 +1233,35 @@ skip_dynamic_range_lvalue_expansion:;  		wire_addr->str = id_addr;  		current_ast_mod->children.push_back(wire_addr);  		current_scope[wire_addr->str] = wire_addr; -		while (wire_addr->simplify(true, false, false, 1, -1, false)) { } +		while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }  		AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));  		wire_data->str = id_data;  		current_ast_mod->children.push_back(wire_data);  		current_scope[wire_data->str] = wire_data; -		while (wire_data->simplify(true, false, false, 1, -1, false)) { } +		while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } -		AstNode *wire_en = new AstNode(AST_WIRE); +		AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));  		wire_en->str = id_en;  		current_ast_mod->children.push_back(wire_en);  		current_scope[wire_en->str] = wire_en; -		while (wire_en->simplify(true, false, false, 1, -1, false)) { } +		while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } -		std::vector<RTLIL::State> x_bits; +		std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en; +		for (int i = 0; i < addr_bits; i++) +			x_bits_addr.push_back(RTLIL::State::Sx);  		for (int i = 0; i < mem_width; i++) -			x_bits.push_back(RTLIL::State::Sx); +			x_bits_data.push_back(RTLIL::State::Sx); +		for (int i = 0; i < mem_width; i++) +			set_bits_en.push_back(RTLIL::State::S1); -		AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false)); +		AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));  		assign_addr->children[0]->str = id_addr; -		AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false)); +		AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));  		assign_data->children[0]->str = id_data; -		AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1)); +		AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));  		assign_en->children[0]->str = id_en;  		AstNode *default_signals = new AstNode(AST_BLOCK); @@ -1106,11 +1273,62 @@ skip_dynamic_range_lvalue_expansion:;  		assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());  		assign_addr->children[0]->str = id_addr; -		assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); -		assign_data->children[0]->str = id_data; +		if (children[0]->children.size() == 2) +		{ +			if (children[0]->children[1]->range_valid) +			{ +				int offset = children[0]->children[1]->range_right; +				int width = children[0]->children[1]->range_left - offset + 1; -		assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); -		assign_en->children[0]->str = id_en; +				std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx); + +				for (int i = 0; i < mem_width; i++) +					set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; + +				assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), +						new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone())); +				assign_data->children[0]->str = id_data; + +				assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); +				assign_en->children[0]->str = id_en; +			} +			else +			{ +				AstNode *the_range = children[0]->children[1]; +				AstNode *left_at_zero_ast = the_range->children[0]->clone(); +				AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone(); +				AstNode *offset_ast = right_at_zero_ast->clone(); + +				while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } +				while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } +				if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) +					log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); +				int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + +				for (int i = 0; i < mem_width; i++) +					set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; + +				assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), +						new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); +				assign_data->children[0]->str = id_data; + +				assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), +						new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); +				assign_en->children[0]->str = id_en; + +				delete left_at_zero_ast; +				delete right_at_zero_ast; +				delete offset_ast; +			} +		} +		else +		{ +			assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); +			assign_data->children[0]->str = id_data; + +			assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); +			assign_en->children[0]->str = id_en; +		}  		newNode = new AstNode(AST_BLOCK);  		newNode->children.push_back(assign_addr); @@ -1137,21 +1355,127 @@ skip_dynamic_range_lvalue_expansion:;  		{  			if (str == "\\$clog2")  			{ +				if (children.size() != 1) +					log_error("System function %s got %d arguments, expected 1 at %s:%d.\n", +							RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); +  				AstNode *buf = children[0]->clone(); -				while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } +				while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }  				if (buf->type != AST_CONSTANT)  					log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);  				RTLIL::Const arg_value = buf->bitsAsConst(); +				if (arg_value.as_bool()) +					arg_value = const_sub(arg_value, 1, false, false, SIZE(arg_value)); +				delete buf; +  				uint32_t result = 0;  				for (size_t i = 0; i < arg_value.bits.size(); i++)  					if (arg_value.bits.at(i) == RTLIL::State::S1) -						result = i; +						result = i + 1;  				newNode = mkconst_int(result, false);  				goto apply_newNode;  			} +			if (str == "\\$ln" || str == "\\$log10" || str == "\\$exp" || str == "\\$sqrt" || str == "\\$pow" || +					str == "\\$floor" || str == "\\$ceil" || str == "\\$sin" || str == "\\$cos" || str == "\\$tan" || +					str == "\\$asin" || str == "\\$acos" || str == "\\$atan" || str == "\\$atan2" || str == "\\$hypot" || +					str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh") +			{ +				bool func_with_two_arguments = str == "\\$pow" || str == "\\$atan2" || str == "\\$hypot"; +				double x = 0, y = 0; + +				if (func_with_two_arguments) { +					if (children.size() != 2) +						log_error("System function %s got %d arguments, expected 2 at %s:%d.\n", +								RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); +				} else { +					if (children.size() != 1) +						log_error("System function %s got %d arguments, expected 1 at %s:%d.\n", +								RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); +				} + +				if (children.size() >= 1) { +					while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } +					if (!children[0]->isConst()) +						log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n", +								RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); +					int child_width_hint = width_hint; +					bool child_sign_hint = sign_hint; +					children[0]->detectSignWidth(child_width_hint, child_sign_hint); +					x = children[0]->asReal(child_sign_hint); +				} + +				if (children.size() >= 2) { +					while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } +					if (!children[1]->isConst()) +						log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n", +								RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); +					int child_width_hint = width_hint; +					bool child_sign_hint = sign_hint; +					children[1]->detectSignWidth(child_width_hint, child_sign_hint); +					y = children[1]->asReal(child_sign_hint); +				} + +				newNode = new AstNode(AST_REALVALUE); +				if (str == "\\$ln")         newNode->realvalue = ::log(x); +				else if (str == "\\$log10") newNode->realvalue = ::log10(x); +				else if (str == "\\$exp")   newNode->realvalue = ::exp(x); +				else if (str == "\\$sqrt")  newNode->realvalue = ::sqrt(x); +				else if (str == "\\$pow")   newNode->realvalue = ::pow(x, y); +				else if (str == "\\$floor") newNode->realvalue = ::floor(x); +				else if (str == "\\$ceil")  newNode->realvalue = ::ceil(x); +				else if (str == "\\$sin")   newNode->realvalue = ::sin(x); +				else if (str == "\\$cos")   newNode->realvalue = ::cos(x); +				else if (str == "\\$tan")   newNode->realvalue = ::tan(x); +				else if (str == "\\$asin")  newNode->realvalue = ::asin(x); +				else if (str == "\\$acos")  newNode->realvalue = ::acos(x); +				else if (str == "\\$atan")  newNode->realvalue = ::atan(x); +				else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y); +				else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y); +				else if (str == "\\$sinh")  newNode->realvalue = ::sinh(x); +				else if (str == "\\$cosh")  newNode->realvalue = ::cosh(x); +				else if (str == "\\$tanh")  newNode->realvalue = ::tanh(x); +				else if (str == "\\$asinh") newNode->realvalue = ::asinh(x); +				else if (str == "\\$acosh") newNode->realvalue = ::acosh(x); +				else if (str == "\\$atanh") newNode->realvalue = ::atanh(x); +				else log_abort(); +				goto apply_newNode; +			} + +			if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) +			{ +				AstNode *dpi_decl = current_scope[str]; + +				std::string rtype, fname; +				std::vector<std::string> argtypes; +				std::vector<AstNode*> args; + +				rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); +				fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); + +				for (int i = 2; i < SIZE(dpi_decl->children); i++) +				{ +					if (i-2 >= SIZE(children)) +						log_error("Insufficient number of arguments in DPI function call at %s:%d.\n", filename.c_str(), linenum); + +					argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str)); +					args.push_back(children.at(i-2)->clone()); +					while (args.back()->simplify(true, false, false, stage, -1, false, true)) { } + +					if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE) +						log_error("Failed to evaluate DPI function with non-constant argument at %s:%d.\n", filename.c_str(), linenum); +				} + +				newNode = dpi_call(rtype, fname, argtypes, args); + +				for (auto arg : args) +					delete arg; + +				goto apply_newNode; +			} +  			if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)  				log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);  		} @@ -1161,22 +1485,47 @@ skip_dynamic_range_lvalue_expansion:;  		}  		AstNode *decl = current_scope[str]; +  		std::stringstream sstr; -		sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++) << "$"; +		sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++) << "$";  		std::string prefix = sstr.str(); +		bool recommend_const_eval = false; +		bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); +		if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count("\\via_celltype")) +		{ +			bool all_args_const = true; +			for (auto child : children) { +				while (child->simplify(true, false, false, 1, -1, false, true)) { } +				if (child->type != AST_CONSTANT) +					all_args_const = false; +			} + +			if (all_args_const) { +				AstNode *func_workspace = current_scope[str]->clone(); +				newNode = func_workspace->eval_const_function(this); +				delete func_workspace; +				goto apply_newNode; +			} + +			if (in_param) +				log_error("Non-constant function call in constant expression at %s:%d.\n", filename.c_str(), linenum); +			if (require_const_eval) +				log_error("Function %s can only be called with constant arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum); +		} +  		size_t arg_count = 0;  		std::map<std::string, std::string> replace_rules;  		if (current_block == NULL)  		{ -			assert(type == AST_FCALL); +			log_assert(type == AST_FCALL);  			AstNode *wire = NULL;  			for (auto child : decl->children)  				if (child->type == AST_WIRE && child->str == str)  					wire = child->clone(); -			assert(wire != NULL); +			log_assert(wire != NULL);  			wire->str = prefix + str;  			wire->port_id = 0; @@ -1184,7 +1533,7 @@ skip_dynamic_range_lvalue_expansion:;  			wire->is_output = false;  			current_ast_mod->children.push_back(wire); -			while (wire->simplify(true, false, false, 1, -1, false)) { } +			while (wire->simplify(true, false, false, 1, -1, false, false)) { }  			AstNode *lvalue = new AstNode(AST_IDENTIFIER);  			lvalue->str = wire->str; @@ -1196,8 +1545,69 @@ skip_dynamic_range_lvalue_expansion:;  			goto replace_fcall_with_id;  		} -		for (auto child : decl->children) +		if (decl->attributes.count("\\via_celltype"))  		{ +			std::string celltype = decl->attributes.at("\\via_celltype")->asAttrConst().decode_string(); +			std::string outport = str; + +			if (celltype.find(' ') != std::string::npos) { +				int pos = celltype.find(' '); +				outport = RTLIL::escape_id(celltype.substr(pos+1)); +				celltype = RTLIL::escape_id(celltype.substr(0, pos)); +			} else +				celltype = RTLIL::escape_id(celltype); + +			AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); +			cell->str = prefix.substr(0, SIZE(prefix)-1); +			cell->children[0]->str = celltype; + +			for (auto attr : decl->attributes) +				if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0) +				{ +					AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone()); +					cell_arg->str = RTLIL::escape_id(attr.first.str().substr(strlen("\\via_celltype_defparam_"))); +					cell->children.push_back(cell_arg); +				} + +			for (auto child : decl->children) +				if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) +				{ +					AstNode *wire = child->clone(); +					wire->str = prefix + wire->str; +					wire->port_id = 0; +					wire->is_input = false; +					wire->is_output = false; +					current_ast_mod->children.push_back(wire); +					while (wire->simplify(true, false, false, 1, -1, false, false)) { } + +					AstNode *wire_id = new AstNode(AST_IDENTIFIER); +					wire_id->str = wire->str; + +					if ((child->is_input || child->is_output) && arg_count < children.size()) +					{ +						AstNode *arg = children[arg_count++]->clone(); +						AstNode *assign = child->is_input ? +								new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) : +								new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone()); + +						for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { +							if (*it != current_block_child) +								continue; +							current_block->children.insert(it, assign); +							break; +						} +					} + +					AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id); +					cell_arg->str = child->str == str ? outport : child->str; +					cell->children.push_back(cell_arg); +				} + +			current_ast_mod->children.push_back(cell); +			goto replace_fcall_with_id; +		} + +		for (auto child : decl->children)  			if (child->type == AST_WIRE)  			{  				AstNode *wire = child->clone(); @@ -1206,16 +1616,18 @@ skip_dynamic_range_lvalue_expansion:;  				wire->is_input = false;  				wire->is_output = false;  				current_ast_mod->children.push_back(wire); -				while (wire->simplify(true, false, false, 1, -1, false)) { } +				while (wire->simplify(true, false, false, 1, -1, false, false)) { }  				replace_rules[child->str] = wire->str; -				if (child->is_input && arg_count < children.size()) +				if ((child->is_input || child->is_output) && arg_count < children.size())  				{  					AstNode *arg = children[arg_count++]->clone();  					AstNode *wire_id = new AstNode(AST_IDENTIFIER);  					wire_id->str = wire->str; -					AstNode *assign = new AstNode(AST_ASSIGN_EQ, wire_id, arg); +					AstNode *assign = child->is_input ? +							new AstNode(AST_ASSIGN_EQ, wire_id, arg) : +							new AstNode(AST_ASSIGN_EQ, arg, wire_id);  					for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {  						if (*it != current_block_child) @@ -1225,10 +1637,12 @@ skip_dynamic_range_lvalue_expansion:;  					}  				}  			} -			else + +		for (auto child : decl->children) +			if (child->type != AST_WIRE)  			{  				AstNode *stmt = child->clone(); -				stmt->replace_ids(replace_rules); +				stmt->replace_ids(prefix, replace_rules);  				for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {  					if (*it != current_block_child) @@ -1237,7 +1651,6 @@ skip_dynamic_range_lvalue_expansion:;  					break;  				}  			} -		}  	replace_fcall_with_id:  		if (type == AST_FCALL) { @@ -1251,7 +1664,7 @@ skip_dynamic_range_lvalue_expansion:;  	}  	// perform const folding when activated -	if (const_fold && newNode == NULL) +	if (const_fold)  	{  		bool string_op;  		std::vector<RTLIL::State> tmp_bits; @@ -1265,13 +1678,29 @@ skip_dynamic_range_lvalue_expansion:;  				if (current_scope[str]->children[0]->type == AST_CONSTANT) {  					if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {  						std::vector<RTLIL::State> data; -						for (int i = children[0]->range_right; i <= children[0]->range_left; i++) -							data.push_back(current_scope[str]->children[0]->bits[i]); +						bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped; +						int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0; +						int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 : +								SIZE(current_scope[str]->children[0]->bits); +						int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right; +						if (param_upto) { +							tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1; +							tmp_range_right = (param_width + 2*param_offset) - children[0]->range_left - 1; +						} +						for (int i = tmp_range_right; i <= tmp_range_left; i++) { +							int index = i - param_offset; +							if (0 <= index && index < param_width) +								data.push_back(current_scope[str]->children[0]->bits[index]); +							else +								data.push_back(RTLIL::State::Sx); +						}  						newNode = mkconst_bits(data, false);  					} else  					if (children.size() == 0)  						newNode = current_scope[str]->children[0]->clone(); -				} +				} else +				if (current_scope[str]->children[0]->isConst()) +					newNode = current_scope[str]->children[0]->clone();  			}  			else if (at_zero && current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)) {  				newNode = mkconst_int(0, sign_hint, width_hint); @@ -1314,6 +1743,9 @@ skip_dynamic_range_lvalue_expansion:;  			if (children[0]->type == AST_CONSTANT) {  				RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);  				newNode = mkconst_bits(y.bits, false); +			} else +			if (children[0]->isConst()) { +				newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1);  			}  			break;  		if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } @@ -1322,6 +1754,12 @@ skip_dynamic_range_lvalue_expansion:;  				RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),  						children[0]->is_signed, children[1]->is_signed, -1);  				newNode = mkconst_bits(y.bits, false); +			} else +			if (children[0]->isConst() && children[1]->isConst()) { +				if (type == AST_LOGIC_AND) +					newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1); +				else +					newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1);  			}  			break;  		if (0) { case AST_SHIFT_LEFT:   const_func = RTLIL::const_shl;  } @@ -1333,6 +1771,10 @@ skip_dynamic_range_lvalue_expansion:;  				RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),  						RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint);  				newNode = mkconst_bits(y.bits, sign_hint); +			} else +			if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) { +				newNode = new AstNode(AST_REALVALUE); +				newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint));  			}  			break;  		if (0) { case AST_LT:  const_func = RTLIL::const_lt; } @@ -1349,6 +1791,20 @@ skip_dynamic_range_lvalue_expansion:;  				RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed),  						children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1);  				newNode = mkconst_bits(y.bits, false); +			} else +			if (children[0]->isConst() && children[1]->isConst()) { +				bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed); +				switch (type) { +				case AST_LT:  newNode = mkconst_int(children[0]->asReal(cmp_signed) <  children[1]->asReal(cmp_signed), false, 1); break; +				case AST_LE:  newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break; +				case AST_EQ:  newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; +				case AST_NE:  newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; +				case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; +				case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; +				case AST_GE:  newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break; +				case AST_GT:  newNode = mkconst_int(children[0]->asReal(cmp_signed) >  children[1]->asReal(cmp_signed), false, 1); break; +				default: log_abort(); +				}  			}  			break;  		if (0) { case AST_ADD: const_func = RTLIL::const_add; } @@ -1360,6 +1816,17 @@ skip_dynamic_range_lvalue_expansion:;  				RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),  						children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);  				newNode = mkconst_bits(y.bits, sign_hint); +			} else +			if (children[0]->isConst() && children[1]->isConst()) { +				newNode = new AstNode(AST_REALVALUE); +				switch (type) { +				case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break; +				case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break; +				case AST_MUL: newNode->realvalue = children[0]->asReal(sign_hint) * children[1]->asReal(sign_hint); break; +				case AST_DIV: newNode->realvalue = children[0]->asReal(sign_hint) / children[1]->asReal(sign_hint); break; +				case AST_MOD: newNode->realvalue = fmod(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); break; +				default: log_abort(); +				}  			}  			break;  		if (0) { case AST_POS: const_func = RTLIL::const_pos; } @@ -1367,37 +1834,105 @@ skip_dynamic_range_lvalue_expansion:;  			if (children[0]->type == AST_CONSTANT) {  				RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);  				newNode = mkconst_bits(y.bits, sign_hint); +			} else +			if (children[0]->isConst()) { +				newNode = new AstNode(AST_REALVALUE); +				if (type == AST_POS) +					newNode->realvalue = +children[0]->asReal(sign_hint); +				else +					newNode->realvalue = -children[0]->asReal(sign_hint); +			} +			break; +		case AST_CASE: +			if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { +				std::vector<AstNode*> new_children; +				new_children.push_back(children[0]); +				for (int i = 1; i < SIZE(children); i++) { +					AstNode *child = children[i]; +					log_assert(child->type == AST_COND); +					for (auto v : child->children) { +						if (v->type == AST_DEFAULT) +							goto keep_const_cond; +						if (v->type == AST_BLOCK) +							continue; +						if (v->type == AST_CONSTANT && v->bits_only_01()) { +							if (v->bits == children[0]->bits) { +								while (i+1 < SIZE(children)) +									delete children[++i]; +								goto keep_const_cond; +							} +							continue; +						} +						goto keep_const_cond; +					} +					if (0) +				keep_const_cond: +						new_children.push_back(child); +					else +						delete child; +				} +				new_children.swap(children);  			}  			break;  		case AST_TERNARY: -			if (children[0]->type == AST_CONSTANT) { +			if (children[0]->isConst()) +			{  				bool found_sure_true = false;  				bool found_maybe_true = false; -				for (auto &bit : children[0]->bits) { -					if (bit == RTLIL::State::S1) -						found_sure_true = true; -					if (bit > RTLIL::State::S1) -						found_maybe_true = true; -				} -				AstNode *choice = NULL; + +				if (children[0]->type == AST_CONSTANT) +					for (auto &bit : children[0]->bits) { +						if (bit == RTLIL::State::S1) +							found_sure_true = true; +						if (bit > RTLIL::State::S1) +							found_maybe_true = true; +					} +				else +					found_sure_true = children[0]->asReal(sign_hint) != 0; + +				AstNode *choice = NULL, *not_choice = NULL;  				if (found_sure_true) -					choice = children[1]; +					choice = children[1], not_choice = children[2];  				else if (!found_maybe_true) -					choice = children[2]; -				if (choice != NULL && choice->type == AST_CONSTANT) { -					RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); -					if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false) -						newNode = mkconst_str(y.bits); -					else -						newNode = mkconst_bits(y.bits, sign_hint); +					choice = children[2], not_choice = children[1]; + +				if (choice != NULL) { +					if (choice->type == AST_CONSTANT) { +						int other_width_hint = width_hint; +						bool other_sign_hint = sign_hint, other_real = false; +						not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real); +						if (other_real) { +							newNode = new AstNode(AST_REALVALUE); +							choice->detectSignWidth(width_hint, sign_hint); +							newNode->realvalue = choice->asReal(sign_hint); +						} else { +							RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); +							if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false) +								newNode = mkconst_str(y.bits); +							else +								newNode = mkconst_bits(y.bits, sign_hint); +						} +					} else +					if (choice->isConst()) { +						newNode = choice->clone(); +					}  				} else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) {  					RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint);  					RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint); -					assert(a.bits.size() == b.bits.size()); +					log_assert(a.bits.size() == b.bits.size());  					for (size_t i = 0; i < a.bits.size(); i++)  						if (a.bits[i] != b.bits[i])  							a.bits[i] = RTLIL::State::Sx;  					newNode = mkconst_bits(a.bits, sign_hint); +				} else if (children[1]->isConst() && children[2]->isConst()) { +					newNode = new AstNode(AST_REALVALUE); +					if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint)) +						newNode->realvalue = children[1]->asReal(sign_hint); +					else +						// IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for +						// the data type in question should be returned if the ?: is ambiguous. The +						// value in Table 7-1 for the 'real' type is 0.0. +						newNode->realvalue = 0.0;  				}  			}  			break; @@ -1431,7 +1966,7 @@ apply_newNode:  		// fprintf(stderr, "----\n");  		// dumpAst(stderr, "- ");  		// newNode->dumpAst(stderr, "+ "); -		assert(newNode != NULL); +		log_assert(newNode != NULL);  		newNode->filename = filename;  		newNode->linenum = linenum;  		newNode->cloneInto(this); @@ -1501,12 +2036,45 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma  }  // rename stuff (used when tasks of functions are instanciated) -void AstNode::replace_ids(std::map<std::string, std::string> &rules) +void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules)  { -	if (type == AST_IDENTIFIER && rules.count(str) > 0) -		str = rules[str]; -	for (auto child : children) -		child->replace_ids(rules); +	if (type == AST_BLOCK) +	{ +		std::map<std::string, std::string> new_rules = rules; +		std::string new_prefix = prefix + str; + +		for (auto child : children) +			if (child->type == AST_WIRE) { +				new_rules[child->str] = new_prefix + child->str; +				child->str = new_prefix + child->str; +			} + +		for (auto child : children) +			if (child->type != AST_WIRE) +				child->replace_ids(new_prefix, new_rules); +	} +	else +	{ +		if (type == AST_IDENTIFIER && rules.count(str) > 0) +			str = rules.at(str); +		for (auto child : children) +			child->replace_ids(prefix, rules); +	} +} + +// helper function for mem2reg_as_needed_pass1 +static void mark_memories_assign_lhs_complex(std::map<AstNode*, std::set<std::string>> &mem2reg_places, +		std::map<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that) +{ +	for (auto &child : that->children) +		mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); + +	if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) { +		AstNode *mem = that->id2ast; +		if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS)) +			mem2reg_places[mem].insert(stringf("%s:%d", that->filename.c_str(), that->linenum)); +		mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; +	}  }  // find memories that should be replaced by registers @@ -1518,6 +2086,10 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>>  	if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)  	{ +		// mark all memories that are used in a complex expression on the left side of an assignment +		for (auto &lhs_child : children[0]->children) +			mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child); +  		if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY)  		{  			AstNode *mem = children[0]->id2ast; @@ -1588,7 +2160,7 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>>  	uint32_t backup_flags = flags;  	flags |= children_flags; -	assert((flags & ~0x000000ff) == 0); +	log_assert((flags & ~0x000000ff) == 0);  	for (auto child : children)  		if (ignore_children_counter > 0) @@ -1617,7 +2189,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  			mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT)  	{  		std::stringstream sstr; -		sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +		sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);  		std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";  		int mem_width, mem_size, addr_bits; @@ -1628,20 +2200,20 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  		wire_addr->is_reg = true;  		wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);  		mod->children.push_back(wire_addr); -		while (wire_addr->simplify(true, false, false, 1, -1, false)) { } +		while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }  		AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));  		wire_data->str = id_data;  		wire_data->is_reg = true;  		wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);  		mod->children.push_back(wire_data); -		while (wire_data->simplify(true, false, false, 1, -1, false)) { } +		while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } -		assert(block != NULL); +		log_assert(block != NULL);  		size_t assign_idx = 0;  		while (assign_idx < block->children.size() && block->children[assign_idx] != this)  			assign_idx++; -		assert(assign_idx < block->children.size()); +		log_assert(assign_idx < block->children.size());  		AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());  		assign_addr->children[0]->str = id_addr; @@ -1654,6 +2226,8 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  				continue;  			AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));  			AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); +			if (children[0]->children.size() == 2) +				assign_reg->children[0]->children.push_back(children[0]->children[1]->clone());  			assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i);  			assign_reg->children[1]->str = id_data;  			cond_node->children[1]->children.push_back(assign_reg); @@ -1670,6 +2244,10 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  	if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0)  	{ +		AstNode *bit_part_sel = NULL; +		if (children.size() == 2) +			bit_part_sel = children[1]->clone(); +  		if (children[0]->children[0]->type == AST_CONSTANT)  		{  			int id = children[0]->children[0]->integer; @@ -1682,7 +2260,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  		else  		{  			std::stringstream sstr; -			sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); +			sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);  			std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";  			int mem_width, mem_size, addr_bits; @@ -1694,7 +2272,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  			if (block)  				wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);  			mod->children.push_back(wire_addr); -			while (wire_addr->simplify(true, false, false, 1, -1, false)) { } +			while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }  			AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));  			wire_data->str = id_data; @@ -1702,7 +2280,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  			if (block)  				wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);  			mod->children.push_back(wire_data); -			while (wire_data->simplify(true, false, false, 1, -1, false)) { } +			while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }  			AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());  			assign_addr->children[0]->str = id_addr; @@ -1736,7 +2314,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  				size_t assign_idx = 0;  				while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))  					assign_idx++; -				assert(assign_idx < block->children.size()); +				log_assert(assign_idx < block->children.size());  				block->children.insert(block->children.begin()+assign_idx, case_node);  				block->children.insert(block->children.begin()+assign_idx, assign_addr);  			} @@ -1753,9 +2331,12 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  			id2ast = NULL;  			str = id_data;  		} + +		if (bit_part_sel) +			children.push_back(bit_part_sel);  	} -	assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); +	log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);  	auto children_list = children;  	for (size_t i = 0; i < children_list.size(); i++) @@ -1765,7 +2346,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *  // calulate memory dimensions  void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)  { -	assert(type == AST_MEMORY); +	log_assert(type == AST_MEMORY);  	mem_width = children[0]->range_left - children[0]->range_right + 1;  	mem_size = children[1]->range_left - children[1]->range_right; @@ -1779,3 +2360,267 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)  		addr_bits++;  } +bool AstNode::has_const_only_constructs(bool &recommend_const_eval) +{ +	if (type == AST_FOR) +		recommend_const_eval = true; +	if (type == AST_WHILE || type == AST_REPEAT) +		return true; +	if (type == AST_FCALL && current_scope.count(str)) +		if (current_scope[str]->has_const_only_constructs(recommend_const_eval)) +			return true; +	for (auto child : children) +		if (child->AstNode::has_const_only_constructs(recommend_const_eval)) +			return true; +	return false; +} + +// helper function for AstNode::eval_const_function() +void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) +{ +	if (type == AST_IDENTIFIER && variables.count(str)) { +		int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); +		if (!children.empty()) { +			if (children.size() != 1 || children.at(0)->type != AST_RANGE) +				log_error("Memory access in constant function is not supported in %s:%d (called from %s:%d).\n", +						filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum); +			children.at(0)->replace_variables(variables, fcall); +			while (simplify(true, false, false, 1, -1, false, true)) { } +			if (!children.at(0)->range_valid) +				log_error("Non-constant range in %s:%d (called from %s:%d).\n", +						filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum); +			offset = std::min(children.at(0)->range_left, children.at(0)->range_right); +			width = std::min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); +		} +		offset -= variables.at(str).offset; +		std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits; +		std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width); +		AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); +		newNode->cloneInto(this); +		delete newNode; +		return; +	} + +	for (auto &child : children) +		child->replace_variables(variables, fcall); +} + +// evaluate functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall) +{ +	std::map<std::string, AstNode*> backup_scope; +	std::map<std::string, AstNode::varinfo_t> variables; +	bool delete_temp_block = false; +	AstNode *block = NULL; + +	size_t argidx = 0; +	for (auto child : children) +	{ +		if (child->type == AST_BLOCK) +		{ +			log_assert(block == NULL); +			block = child; +			continue; +		} + +		if (child->type == AST_WIRE) +		{ +			while (child->simplify(true, false, false, 1, -1, false, true)) { } +			if (!child->range_valid) +				log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n", +						child->str.c_str(), child->filename.c_str(), child->linenum, fcall->filename.c_str(), fcall->linenum); +			variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); +			variables[child->str].offset = std::min(child->range_left, child->range_right); +			variables[child->str].is_signed = child->is_signed; +			if (child->is_input && argidx < fcall->children.size()) +				variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size()); +			backup_scope[child->str] = current_scope[child->str]; +			current_scope[child->str] = child; +			continue; +		} + +		log_assert(block == NULL); +		delete_temp_block = true; +		block = new AstNode(AST_BLOCK); +		block->children.push_back(child->clone()); +	} + +	log_assert(block != NULL); +	log_assert(variables.count(str)); + +	while (!block->children.empty()) +	{ +		AstNode *stmt = block->children.front(); + +#if 0 +		log("-----------------------------------\n"); +		for (auto &it : variables) +			log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val)); +		stmt->dumpAst(NULL, "stmt> "); +#endif + +		if (stmt->type == AST_ASSIGN_EQ) +		{ +			stmt->children.at(1)->replace_variables(variables, fcall); +			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + +			if (stmt->type != AST_ASSIGN_EQ) +				continue; + +			if (stmt->children.at(1)->type != AST_CONSTANT) +				log_error("Non-constant expression in constant function at %s:%d (called from %s:%d). X\n", +						stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); + +			if (stmt->children.at(0)->type != AST_IDENTIFIER) +				log_error("Unsupported composite left hand side in constant function at %s:%d (called from %s:%d).\n", +						stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); + +			if (!variables.count(stmt->children.at(0)->str)) +				log_error("Assignment to non-local variable in constant function at %s:%d (called from %s:%d).\n", +						stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); + +			if (stmt->children.at(0)->children.empty()) { +				variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); +			} else { +				AstNode *range = stmt->children.at(0)->children.at(0); +				if (!range->range_valid) +					log_error("Non-constant range in %s:%d (called from %s:%d).\n", +							range->filename.c_str(), range->linenum, fcall->filename.c_str(), fcall->linenum); +				int offset = std::min(range->range_left, range->range_right); +				int width = std::abs(range->range_left - range->range_right) + 1; +				varinfo_t &v = variables[stmt->children.at(0)->str]; +				RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); +				for (int i = 0; i < width; i++) +					v.val.bits.at(i+offset-v.offset) = r.bits.at(i); +			} + +			delete block->children.front(); +			block->children.erase(block->children.begin()); +			continue; +		} + +		if (stmt->type == AST_FOR) +		{ +			block->children.insert(block->children.begin(), stmt->children.at(0)); +			stmt->children.at(3)->children.push_back(stmt->children.at(2)); +			stmt->children.erase(stmt->children.begin() + 2); +			stmt->children.erase(stmt->children.begin()); +			stmt->type = AST_WHILE; +			continue; +		} + +		if (stmt->type == AST_WHILE) +		{ +			AstNode *cond = stmt->children.at(0)->clone(); +			cond->replace_variables(variables, fcall); +			while (cond->simplify(true, false, false, 1, -1, false, true)) { } + +			if (cond->type != AST_CONSTANT) +				log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n", +						stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); + +			if (cond->asBool()) { +				block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); +			} else { +				delete block->children.front(); +				block->children.erase(block->children.begin()); +			} + +			delete cond; +			continue; +		} + +		if (stmt->type == AST_REPEAT) +		{ +			AstNode *num = stmt->children.at(0)->clone(); +			num->replace_variables(variables, fcall); +			while (num->simplify(true, false, false, 1, -1, false, true)) { } + +			if (num->type != AST_CONSTANT) +				log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n", +						stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); + +			block->children.erase(block->children.begin()); +			for (int i = 0; i < num->bitsAsConst().as_int(); i++) +				block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); + +			delete stmt; +			delete num; +			continue; +		} + +		if (stmt->type == AST_CASE) +		{ +			AstNode *expr = stmt->children.at(0)->clone(); +			expr->replace_variables(variables, fcall); +			while (expr->simplify(true, false, false, 1, -1, false, true)) { } + +			AstNode *sel_case = NULL; +			for (size_t i = 1; i < stmt->children.size(); i++) +			{ +				bool found_match = false; +				log_assert(stmt->children.at(i)->type == AST_COND); + +				if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { +					sel_case = stmt->children.at(i)->children.back(); +					continue; +				} + +				for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) +				{ +					AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); +					cond->replace_variables(variables, fcall); + +					cond = new AstNode(AST_EQ, expr->clone(), cond); +					while (cond->simplify(true, false, false, 1, -1, false, true)) { } + +					if (cond->type != AST_CONSTANT) +						log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n", +								stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); + +					found_match = cond->asBool(); +					delete cond; +				} + +				if (found_match) { +					sel_case = stmt->children.at(i)->children.back(); +					break; +				} +			} + +			block->children.erase(block->children.begin()); +			if (sel_case) +				block->children.insert(block->children.begin(), sel_case->clone()); +			delete stmt; +			delete expr; +			continue; +		} + +		if (stmt->type == AST_BLOCK) +		{ +			block->children.erase(block->children.begin()); +			block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); +			stmt->children.clear(); +			delete stmt; +			continue; +		} + +		log_error("Unsupported language construct in constant function at %s:%d (called from %s:%d).\n", +				stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum); +		log_abort(); +	} + +	if (delete_temp_block) +		delete block; + +	for (auto &it : backup_scope) +		if (it.second == NULL) +			current_scope.erase(it.first); +		else +			current_scope[it.first] = it.second; + +	return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); +} + +YOSYS_NAMESPACE_END +  | 
