diff options
Diffstat (limited to 'frontends')
| -rw-r--r-- | frontends/ast/ast.cc | 5 | ||||
| -rw-r--r-- | frontends/ast/ast.h | 11 | ||||
| -rw-r--r-- | frontends/ast/dpicall.cc | 17 | ||||
| -rw-r--r-- | frontends/ast/genrtlil.cc | 31 | ||||
| -rw-r--r-- | frontends/ast/simplify.cc | 433 | ||||
| -rw-r--r-- | frontends/verific/verific.cc | 58 | ||||
| -rw-r--r-- | frontends/verilog/preproc.cc | 8 | ||||
| -rw-r--r-- | frontends/verilog/verilog_frontend.cc | 3 | ||||
| -rw-r--r-- | frontends/verilog/verilog_parser.y | 172 | 
9 files changed, 459 insertions, 279 deletions
| diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index c8183580b..dc47420af 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -548,9 +548,9 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const  		break;  	case AST_CASE: -		if (!children.empty() && children[0]->type == AST_CONDX) +		if (children.size() > 1 && children[1]->type == AST_CONDX)  			fprintf(f, "%s" "casex (", indent.c_str()); -		else if (!children.empty() && children[0]->type == AST_CONDZ) +		else if (children.size() > 1 && children[1]->type == AST_CONDZ)  			fprintf(f, "%s" "casez (", indent.c_str());  		else  			fprintf(f, "%s" "case (", indent.c_str()); @@ -1511,6 +1511,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr  		}  	} else { +		modname = new_modname;  		log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());  	} diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 1b8ed22ca..d8818df31 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -250,9 +250,10 @@ 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 in_param); +		void replace_result_wire_name_in_function(const std::string &from, const std::string &to);  		AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); -		void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope = true); -		void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); +		void expand_genblock(const std::string &prefix); +		void label_genblks(std::set<std::string>& existing, int &counter);  		void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,  				dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);  		bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); @@ -263,9 +264,9 @@ namespace AST  		// 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); +		bool has_const_only_constructs(); +		bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); +		AstNode *eval_const_function(AstNode *fcall, bool must_succeed);  		bool is_simple_const_expr();  		std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e241142d3..948c9083c 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -67,7 +67,7 @@ static ffi_fptr resolve_fn (std::string symbol_name)  AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args)  {  	AST::AstNode *newNode = nullptr; -	union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; +	union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1];  	ffi_type *types [args.size() + 1];  	void *values [args.size() + 1];  	ffi_cif cif; @@ -92,6 +92,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,  			value_store[i].i32 = args[i]->asInt(args[i]->is_signed);  			values[i] = &value_store[i].i32;  			types[i] = &ffi_type_sint32; +		} else if (argtypes[i] == "chandle") { +			log("  arg %d (%s): %llx\n", i, argtypes[i].c_str(), (unsigned long long)args[i]->asInt(false)); +			value_store[i].ptr = (void *)args[i]->asInt(args[i]->is_signed); +			values[i] = &value_store[i].ptr; +			types[i] = &ffi_type_pointer;  		} else {  			log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i);  		} @@ -106,6 +111,9 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,          } else if (rtype == "real") {                  types[args.size()] = &ffi_type_double;                  values[args.size()] = &value_store[args.size()].f64; +        } else if (rtype == "chandle") { +                types[args.size()] = &ffi_type_pointer; +                values[args.size()] = &value_store[args.size()].ptr;          } else {                  log_error("invalid rtype '%s'.\n", rtype.c_str());          } @@ -123,6 +131,13 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,  		newNode = new AstNode(AST_REALVALUE);  		newNode->realvalue = value_store[args.size()].f32;  		log("  return realvalue: %g\n", newNode->asReal(true)); +	} else if (rtype == "chandle") { +		uint64_t rawval = (uint64_t)value_store[args.size()].ptr; +		std::vector<RTLIL::State> bits(64); +		for (int i = 0; i < 64; i++) +			bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0; +		newNode = AstNode::mkconst_bits(bits, false); +		log("  return chandle: %llx\n", (unsigned long long)newNode->asInt(false));  	} else {  		newNode = AstNode::mkconst_int(value_store[args.size()].i32, false);  		log("  return integer: %lld\n", (long long)newNode->asInt(true)); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index e878d0dd2..24f5e1bef 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -49,6 +49,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);  	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	wire->is_signed = that->is_signed;  	if (gen_attributes)  		for (auto &attr : that->attributes) { @@ -80,6 +81,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width);  	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	wire->is_signed = that->is_signed;  	if (that != NULL)  		for (auto &attr : that->attributes) { @@ -106,6 +108,7 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);  	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	wire->is_signed = that->is_signed;  	for (auto &attr : that->attributes) {  		if (attr.second->type != AST_CONSTANT) @@ -140,6 +143,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const  	RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size());  	wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); +	wire->is_signed = that->is_signed;  	for (auto &attr : that->attributes) {  		if (attr.second->type != AST_CONSTANT) @@ -1048,6 +1052,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			RTLIL::Const val = children[0]->bitsAsConst();  			RTLIL::Wire *wire = current_module->addWire(str, GetSize(val));  			current_module->connect(wire, val); +			wire->is_signed = children[0]->is_signed;  			wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column);  			wire->attributes[type == AST_PARAMETER ? ID::parameter : ID::localparam] = 1; @@ -1549,6 +1554,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  			int mem_width, mem_size, addr_bits;  			is_signed = id2ast->is_signed; +			wire->is_signed = is_signed;  			id2ast->meminfo(mem_width, mem_size, addr_bits);  			RTLIL::SigSpec addr_sig = children[0]->genRTLIL(); @@ -1721,8 +1727,29 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  				}  				if (child->type == AST_ARGUMENT) {  					RTLIL::SigSpec sig; -					if (child->children.size() > 0) -						sig = child->children[0]->genRTLIL(); +					if (child->children.size() > 0) { +						AstNode *arg = child->children[0]; +						int local_width_hint = -1; +						bool local_sign_hint = false; +						// don't inadvertently attempt to detect the width of interfaces +						if (arg->type != AST_IDENTIFIER || !arg->id2ast || arg->id2ast->type != AST_CELL) +							arg->detectSignWidth(local_width_hint, local_sign_hint); +						sig = arg->genRTLIL(local_width_hint, local_sign_hint); +						log_assert(local_sign_hint == arg->is_signed); +						if (sig.is_wire()) { +							// if the resulting SigSpec is a wire, its +							// signedness should match that of the AstNode +							log_assert(arg->is_signed == sig.as_wire()->is_signed); +						} else if (arg->is_signed) { +							// non-trivial signed nodes are indirected through +							// signed wires to enable sign extension +							RTLIL::IdString wire_name = NEW_ID; +							RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig)); +							wire->is_signed = true; +							current_module->connect(wire, sig); +							sig = wire; +						} +					}  					if (child->str.size() == 0) {  						char buf[100];  						snprintf(buf, 100, "$%d", ++port_counter); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index fb6623f02..402b7257b 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var)  	return true;  } +static std::string prefix_id(const std::string &prefix, const std::string &str) +{ +	log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); +	log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); +	log_assert(prefix.back() == '.'); +	if (str.front() == '\\') +		return prefix + str.substr(1); +	return prefix + str; +} +  // convert the AST into a simpler AST that has all parameters substituted by their  // values, unrolled for-loops, expanded generate blocks, etc. when this function  // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -748,6 +758,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	// also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")  	if (type == AST_MODULE) {  		current_scope.clear(); +		std::set<std::string> existing; +		int counter = 0; +		label_genblks(existing, counter);  		std::map<std::string, AstNode*> this_wire_scope;  		for (size_t i = 0; i < children.size(); i++) {  			AstNode *node = children[i]; @@ -1325,6 +1338,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {  				// replace with wire representing the packed structure  				newNode = make_packed_struct(template_node, str); +				// add original input/output attribute to resolved wire +				newNode->is_input = this->is_input; +				newNode->is_output = this->is_output;  				current_scope[str] = this;  				goto apply_newNode;  			} @@ -1847,19 +1863,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			// expand body  			int index = varbuf->children[0]->integer; -			if (body_ast->type == AST_GENBLOCK) -				buf = body_ast->clone(); -			else -				buf = new AstNode(AST_GENBLOCK, body_ast->clone()); -			if (buf->str.empty()) { -				std::stringstream sstr; -				sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++); -				buf->str = sstr.str(); -			} -			std::map<std::string, std::string> name_map; +			log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); +			log_assert(!body_ast->str.empty()); +			buf = body_ast->clone(); +  			std::stringstream sstr;  			sstr << buf->str << "[" << index << "]."; -			buf->expand_genblock(varbuf->str, sstr.str(), name_map); +			std::string prefix = sstr.str(); + +			// create a scoped localparam for the current value of the loop variable +			AstNode *local_index = varbuf->clone(); +			size_t pos = local_index->str.rfind('.'); +			if (pos != std::string::npos) // remove outer prefix +				local_index->str = "\\" + local_index->str.substr(pos + 1); +			local_index->str = prefix_id(prefix, local_index->str); +			current_scope[local_index->str] = local_index; +			current_ast_mod->children.push_back(local_index); + +			buf->expand_genblock(prefix);  			if (type == AST_GENFOR) {  				for (size_t i = 0; i < buf->children.size(); i++) { @@ -1907,14 +1928,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	{  		for (size_t i = 0; i < children.size(); i++)  			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) -				log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); +			{ +				log_assert(!VERILOG_FRONTEND::sv_mode); +				log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); +			}  	}  	// transform block with name  	if (type == AST_BLOCK && !str.empty())  	{ -		std::map<std::string, std::string> name_map; -		expand_genblock(std::string(), str + ".", name_map); +		expand_genblock(str + ".");  		std::vector<AstNode*> new_children;  		for (size_t i = 0; i < children.size(); i++) @@ -1934,8 +1957,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (type == AST_GENBLOCK && children.size() != 0)  	{  		if (!str.empty()) { -			std::map<std::string, std::string> name_map; -			expand_genblock(std::string(), str + ".", name_map); +			expand_genblock(str + ".");  		}  		for (size_t i = 0; i < children.size(); i++) { @@ -1971,8 +1993,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				buf = new AstNode(AST_GENBLOCK, buf);  			if (!buf->str.empty()) { -				std::map<std::string, std::string> name_map; -				buf->expand_genblock(std::string(), buf->str + ".", name_map); +				buf->expand_genblock(buf->str + ".");  			}  			for (size_t i = 0; i < buf->children.size(); i++) { @@ -2050,8 +2071,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  			buf = selected_case->clone();  			if (!buf->str.empty()) { -				std::map<std::string, std::string> name_map; -				buf->expand_genblock(std::string(), buf->str + ".", name_map); +				buf->expand_genblock(buf->str + ".");  			}  			for (size_t i = 0; i < buf->children.size(); i++) { @@ -3151,16 +3171,19 @@ skip_dynamic_range_lvalue_expansion:;  				log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str());  		} -		AstNode *decl = current_scope[str];  		std::stringstream sstr; -		sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; +		sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.';  		std::string prefix = sstr.str(); -		bool recommend_const_eval = false; -		bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); -		if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) +		AstNode *decl = current_scope[str]; +		decl = decl->clone(); +		decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion +		decl->expand_genblock(prefix); + +		if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype))  		{ +			bool require_const_eval = decl->has_const_only_constructs();  			bool all_args_const = true;  			for (auto child : children) {  				while (child->simplify(true, false, false, 1, -1, false, true)) { } @@ -3169,10 +3192,14 @@ skip_dynamic_range_lvalue_expansion:;  			}  			if (all_args_const) { -				AstNode *func_workspace = current_scope[str]->clone(); -				newNode = func_workspace->eval_const_function(this); +				AstNode *func_workspace = decl->clone(); +				func_workspace->str = prefix_id(prefix, "$result"); +				newNode = func_workspace->eval_const_function(this, in_param || require_const_eval);  				delete func_workspace; -				goto apply_newNode; +				if (newNode) { +					delete decl; +					goto apply_newNode; +				}  			}  			if (in_param) @@ -3182,8 +3209,6 @@ skip_dynamic_range_lvalue_expansion:;  		}  		size_t arg_count = 0; -		std::map<std::string, std::string> replace_rules; -		vector<AstNode*> added_mod_children;  		dict<std::string, AstNode*> wire_cache;  		vector<AstNode*> new_stmts;  		vector<AstNode*> output_assignments; @@ -3193,16 +3218,17 @@ skip_dynamic_range_lvalue_expansion:;  			log_assert(type == AST_FCALL);  			AstNode *wire = NULL; +			std::string res_name = prefix_id(prefix, "$result");  			for (auto child : decl->children) -				if (child->type == AST_WIRE && child->str == str) +				if (child->type == AST_WIRE && child->str == res_name)  					wire = child->clone();  			log_assert(wire != NULL); -			wire->str = prefix + str;  			wire->port_id = 0;  			wire->is_input = false;  			wire->is_output = false; +			current_scope[wire->str] = wire;  			current_ast_mod->children.push_back(wire);  			while (wire->simplify(true, false, false, 1, -1, false, false)) { } @@ -3246,7 +3272,6 @@ skip_dynamic_range_lvalue_expansion:;  				if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))  				{  					AstNode *wire = child->clone(); -					wire->str = prefix + wire->str;  					wire->port_id = 0;  					wire->is_input = false;  					wire->is_output = false; @@ -3308,7 +3333,6 @@ skip_dynamic_range_lvalue_expansion:;  				else  				{  					wire = child->clone(); -					wire->str = prefix + wire->str;  					wire->port_id = 0;  					wire->is_input = false;  					wire->is_output = false; @@ -3319,15 +3343,11 @@ skip_dynamic_range_lvalue_expansion:;  					wire_cache[child->str] = wire; +					current_scope[wire->str] = wire;  					current_ast_mod->children.push_back(wire); -					added_mod_children.push_back(wire);  				} -				if (child->type == AST_WIRE) -					while (wire->simplify(true, false, false, 1, -1, false, false)) { } - -				replace_rules[child->str] = wire->str; -				current_scope[wire->str] = wire; +				while (wire->simplify(true, false, false, 1, -1, false, false)) { }  				if ((child->is_input || child->is_output) && arg_count < children.size())  				{ @@ -3337,6 +3357,25 @@ skip_dynamic_range_lvalue_expansion:;  						wire->type = AST_LOCALPARAM;  						wire->attributes.erase(ID::nosync);  						wire->children.insert(wire->children.begin(), arg->clone()); +						// args without a range implicitly have width 1 +						if (wire->children.back()->type != AST_RANGE) { +							// check if this wire is redeclared with an explicit size +							bool uses_explicit_size = false; +							for (const AstNode *other_child : decl->children) +								if (other_child->type == AST_WIRE && child->str == other_child->str +										&& !other_child->children.empty() +										&& other_child->children.back()->type == AST_RANGE) { +									uses_explicit_size = true; +									break; +								} +							if (!uses_explicit_size) { +								AstNode* range = new AstNode(); +								range->type = AST_RANGE; +								wire->children.push_back(range); +								range->children.push_back(mkconst_int(0, true)); +								range->children.push_back(mkconst_int(0, true)); +							} +						}  						continue;  					}  					AstNode *wire_id = new AstNode(AST_IDENTIFIER); @@ -3352,18 +3391,9 @@ skip_dynamic_range_lvalue_expansion:;  				}  			} -		for (auto child : added_mod_children) { -			child->replace_ids(prefix, replace_rules); -			while (child->simplify(true, false, false, 1, -1, false, false)) { } -		} -  		for (auto child : decl->children)  			if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) -			{ -				AstNode *stmt = child->clone(); -				stmt->replace_ids(prefix, replace_rules); -				new_stmts.push_back(stmt); -			} +				new_stmts.push_back(child->clone());  		new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); @@ -3376,10 +3406,11 @@ skip_dynamic_range_lvalue_expansion:;  		}  	replace_fcall_with_id: +		delete decl;  		if (type == AST_FCALL) {  			delete_children();  			type = AST_IDENTIFIER; -			str = prefix + str; +			str = prefix_id(prefix, "$result");  		}  		if (type == AST_TCALL)  			str = ""; @@ -3427,7 +3458,14 @@ replace_fcall_later:;  				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)) { +			else if (at_zero && current_scope.count(str) > 0) { +				AstNode *node = current_scope[str]; +				if (node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY) +					newNode = mkconst_int(0, sign_hint, width_hint); +			} +			break; +		case AST_MEMRD: +			if (at_zero) {  				newNode = mkconst_int(0, sign_hint, width_hint);  			}  			break; @@ -3683,12 +3721,12 @@ apply_newNode:  	return did_something;  } -static void replace_result_wire_name_in_function(AstNode *node, std::string &from, std::string &to) +void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to)  { -	for (auto &it : node->children) -		replace_result_wire_name_in_function(it, from, to); -	if (node->str == from) -		node->str = to; +	for (AstNode *child : children) +		child->replace_result_wire_name_in_function(from, to); +	if (str == from && type != AST_FCALL && type != AST_TCALL) +		str = to;  }  // replace a readmem[bh] TCALL ast node with a block of memory assignments @@ -3823,65 +3861,54 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m  	return block;  } -// annotate the names of all wires and other named objects in a generate block -void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) +// annotate the names of all wires and other named objects in a named generate +// or procedural block; nested blocks are themselves annotated such that the +// prefix is carried forward, but resolution of their children is deferred +void AstNode::expand_genblock(const std::string &prefix)  { -	// `original_scope` defaults to false, and is used to prevent the premature -	// prefixing of items in named sub-blocks - -	if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { -		if (children.empty()) { -			current_scope[index_var]->children[0]->cloneInto(this); -		} else { -			AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); -			p->str = stringf("$genval$%d", autoidx++); -			current_ast_mod->children.push_back(p); -			str = p->str; -			id2ast = p; -		} -	} -  	if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { -		if (name_map.count(str) > 0) { -			str = name_map[str]; -		} else { -			// remap the prefix of this ident if it is a local generate scope -			size_t pos = str.rfind('.'); -			if (pos != std::string::npos) { -				std::string existing_prefix = str.substr(0, pos); -				if (name_map.count(existing_prefix) > 0) { -					str = name_map[existing_prefix] + str.substr(pos); -				} +		log_assert(!str.empty()); + +		// search starting in the innermost scope and then stepping outward +		for (size_t ppos = prefix.size() - 1; ppos; --ppos) { +			if (prefix.at(ppos) != '.') continue; + +			std::string new_prefix = prefix.substr(0, ppos + 1); +			auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { +				std::string new_name = prefix_id(new_prefix, ident); +				if (current_scope.count(new_name)) +					return new_name; +				return {}; +			}; + +			// attempt to resolve the full identifier +			std::string resolved = attempt_resolve(str); +			if (!resolved.empty()) { +				str = resolved; +				break;  			} -		} -	} - -	std::map<std::string, std::string> backup_name_map; -	auto prefix_node = [&](AstNode* child) { -		if (backup_name_map.size() == 0) -			backup_name_map = name_map; +			// attempt to resolve hierarchical prefixes within the identifier, +			// as the prefix could refer to a local scope which exists but +			// hasn't yet been elaborated +			for (size_t spos = str.size() - 1; spos; --spos) { +				if (str.at(spos) != '.') continue; +				resolved = attempt_resolve(str.substr(0, spos)); +				if (!resolved.empty()) { +					str = resolved + str.substr(spos); +					ppos = 1; // break outer loop +					break; +				} +			} -		// if within a nested scope -		if (!original_scope) { -			// this declaration shadows anything in the parent scope(s) -			name_map[child->str] = child->str; -			return;  		} +	} -		std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; -		size_t pos = child->str.rfind('.'); -		if (pos == std::string::npos) -			pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; -		else -			pos = pos + 1; -		new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); -		if (new_name[0] != '$' && new_name[0] != '\\') -			new_name = prefix[0] + new_name; - -		name_map[child->str] = new_name; +	auto prefix_node = [&prefix](AstNode* child) { +		if (child->str.empty()) return; +		std::string new_name = prefix_id(prefix, child->str);  		if (child->type == AST_FUNCTION) -			replace_result_wire_name_in_function(child, child->str, new_name); +			child->replace_result_wire_name_in_function(child->str, new_name);  		else  			child->str = new_name;  		current_scope[new_name] = child; @@ -3931,43 +3958,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma  			continue;  		// functions/tasks may reference wires, constants, etc. in this scope  		if (child->type == AST_FUNCTION || child->type == AST_TASK) -			child->expand_genblock(index_var, prefix, name_map, false); -		// continue prefixing if this child block is anonymous -		else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) -			child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); -		else -			child->expand_genblock(index_var, prefix, name_map, original_scope); -	} - +			continue; +		// named blocks pick up the current prefix and will expanded later +		if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) +			continue; -	if (backup_name_map.size() > 0) -		name_map.swap(backup_name_map); +		child->expand_genblock(prefix); +	}  } -// rename stuff (used when tasks of functions are instantiated) -void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) +// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or +// IEEE 1800-2017 Section 27.6 +void AstNode::label_genblks(std::set<std::string>& existing, int &counter)  { -	if (type == AST_BLOCK) -	{ -		std::map<std::string, std::string> new_rules = rules; -		std::string new_prefix = prefix + str; - -		for (auto child : children) -			if (child->type == AST_WIRE) { -				new_rules[child->str] = new_prefix + child->str; -				child->str = new_prefix + child->str; -			} +	switch (type) { +	case AST_GENIF: +	case AST_GENFOR: +	case AST_GENCASE: +		// seeing a proper generate control flow construct increments the +		// counter once +		++counter; +		for (AstNode *child : children) +			child->label_genblks(existing, counter); +		break; -		for (auto child : children) -			if (child->type != AST_WIRE) -				child->replace_ids(new_prefix, new_rules); +	case AST_GENBLOCK: { +		// if this block is unlabeled, generate its corresponding unique name +		for (int padding = 0; str.empty(); ++padding) { +			std::string candidate = "\\genblk"; +			for (int i = 0; i < padding; ++i) +				candidate += '0'; +			candidate += std::to_string(counter); +			if (!existing.count(candidate)) +				str = candidate; +		} +		// within a genblk, the counter starts fresh +		std::set<std::string> existing_local = existing; +		int counter_local = 0; +		for (AstNode *child : children) +			child->label_genblks(existing_local, counter_local); +		break;  	} -	else -	{ -		if (type == AST_IDENTIFIER && rules.count(str) > 0) -			str = rules.at(str); -		for (auto child : children) -			child->replace_ids(prefix, rules); + +	default: +		// track names which could conflict with implicit genblk names +		if (str.rfind("\\genblk", 0) == 0) +			existing.insert(str); +		for (AstNode *child : children) +			child->label_genblks(existing, counter); +		break;  	}  } @@ -4459,17 +4498,12 @@ bool AstNode::detect_latch(const std::string &var)  	}  } -bool AstNode::has_const_only_constructs(bool &recommend_const_eval) +bool AstNode::has_const_only_constructs()  { -	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)) +		if (child->has_const_only_constructs())  			return true;  	return false;  } @@ -4485,19 +4519,26 @@ bool AstNode::is_simple_const_expr()  }  // helper function for AstNode::eval_const_function() -void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) +bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed)  {  	if (type == AST_IDENTIFIER && variables.count(str)) {  		int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();  		if (!children.empty()) { -			if (children.size() != 1 || children.at(0)->type != AST_RANGE) +			if (children.size() != 1 || children.at(0)->type != AST_RANGE) { +				if (!must_succeed) +					return false;  				log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); -			children.at(0)->replace_variables(variables, fcall); +			} +			if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) +				return false;  			while (simplify(true, false, false, 1, -1, false, true)) { } -			if (!children.at(0)->range_valid) +			if (!children.at(0)->range_valid) { +				if (!must_succeed) +					return false;  				log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			}  			offset = min(children.at(0)->range_left, children.at(0)->range_right);  			width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);  		} @@ -4507,19 +4548,22 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia  		AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);  		newNode->cloneInto(this);  		delete newNode; -		return; +		return true;  	}  	for (auto &child : children) -		child->replace_variables(variables, fcall); +		if (!child->replace_variables(variables, fcall, must_succeed)) +			return false; +	return true;  } -// evaluate functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall) +// attempt to statically evaluate a functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)  { -	std::map<std::string, AstNode*> backup_scope; +	std::map<std::string, AstNode*> backup_scope = current_scope;  	std::map<std::string, AstNode::varinfo_t> variables;  	AstNode *block = new AstNode(AST_BLOCK); +	AstNode *result = nullptr;  	size_t argidx = 0;  	for (auto child : children) @@ -4541,9 +4585,12 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_WIRE)  		{  			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } -			if (!stmt->range_valid) +			if (!stmt->range_valid) { +				if (!must_succeed) +					goto finished;  				log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n",  						stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			}  			variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1);  			variables[stmt->str].offset = min(stmt->range_left, stmt->range_right);  			variables[stmt->str].is_signed = stmt->is_signed; @@ -4557,8 +4604,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  					variables[stmt->str].val = arg_node->realAsConst(width);  				}  			} -			if (!backup_scope.count(stmt->str)) -				backup_scope[stmt->str] = current_scope[stmt->str];  			current_scope[stmt->str] = stmt;  			block->children.erase(block->children.begin()); @@ -4571,8 +4616,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		{  			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } -			if (!backup_scope.count(stmt->str)) -				backup_scope[stmt->str] = current_scope[stmt->str];  			current_scope[stmt->str] = stmt;  			block->children.erase(block->children.begin()); @@ -4583,32 +4626,46 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		{  			if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 &&  					stmt->children.at(0)->children.at(0)->type == AST_RANGE) -				stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); -			stmt->children.at(1)->replace_variables(variables, fcall); +				if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) +					goto finished; +			if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (stmt->simplify(true, false, false, 1, -1, false, true)) { }  			if (stmt->type != AST_ASSIGN_EQ)  				continue; -			if (stmt->children.at(1)->type != AST_CONSTANT) +			if (stmt->children.at(1)->type != AST_CONSTANT) { +				if (!must_succeed) +					goto finished;  				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			} -			if (stmt->children.at(0)->type != AST_IDENTIFIER) +			if (stmt->children.at(0)->type != AST_IDENTIFIER) { +				if (!must_succeed) +					goto finished;  				log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			} -			if (!variables.count(stmt->children.at(0)->str)) +			if (!variables.count(stmt->children.at(0)->str)) { +				if (!must_succeed) +					goto finished;  				log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			}  			if (stmt->children.at(0)->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) +				if (!range->range_valid) { +					if (!must_succeed) +						goto finished;  					log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n",  							fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +				}  				int offset = min(range->range_left, range->range_right);  				int width = std::abs(range->range_left - range->range_right) + 1;  				varinfo_t &v = variables[stmt->children.at(0)->str]; @@ -4635,12 +4692,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_WHILE)  		{  			AstNode *cond = stmt->children.at(0)->clone(); -			cond->replace_variables(variables, fcall); +			if (!cond->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (cond->simplify(true, false, false, 1, -1, false, true)) { } -			if (cond->type != AST_CONSTANT) +			if (cond->type != AST_CONSTANT) { +				if (!must_succeed) +					goto finished;  				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			}  			if (cond->asBool()) {  				block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -4656,12 +4717,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_REPEAT)  		{  			AstNode *num = stmt->children.at(0)->clone(); -			num->replace_variables(variables, fcall); +			if (!num->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (num->simplify(true, false, false, 1, -1, false, true)) { } -			if (num->type != AST_CONSTANT) +			if (num->type != AST_CONSTANT) { +				if (!must_succeed) +					goto finished;  				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n",  						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +			}  			block->children.erase(block->children.begin());  			for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -4675,7 +4740,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_CASE)  		{  			AstNode *expr = stmt->children.at(0)->clone(); -			expr->replace_variables(variables, fcall); +			if (!expr->replace_variables(variables, fcall, must_succeed)) +				goto finished;  			while (expr->simplify(true, false, false, 1, -1, false, true)) { }  			AstNode *sel_case = NULL; @@ -4692,14 +4758,18 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  				for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++)  				{  					AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); -					cond->replace_variables(variables, fcall); +					if (!cond->replace_variables(variables, fcall, must_succeed)) +						goto finished;  					cond = new AstNode(AST_EQ, expr->clone(), cond);  					while (cond->simplify(true, false, false, 1, -1, false, true)) { } -					if (cond->type != AST_CONSTANT) +					if (cond->type != AST_CONSTANT) { +						if (!must_succeed) +							goto finished;  						log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n",  								fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); +					}  					found_match = cond->asBool();  					delete cond; @@ -4721,6 +4791,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  		if (stmt->type == AST_BLOCK)  		{ +			if (!stmt->str.empty()) +				stmt->expand_genblock(stmt->str + "."); +  			block->children.erase(block->children.begin());  			block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());  			stmt->children.clear(); @@ -4728,20 +4801,20 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)  			continue;  		} +		if (!must_succeed) +			goto finished;  		log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n",  				fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column);  		log_abort();  	} -	delete block; +	result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); -	for (auto &it : backup_scope) -		if (it.second == NULL) -			current_scope.erase(it.first); -		else -			current_scope[it.first] = it.second; +finished: +	delete block; +	current_scope = backup_scope; -	return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); +	return result;  }  void AstNode::allocateDefaultEnumValues() diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 31c77d39c..7aa3ebcbb 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -49,14 +49,17 @@ USING_YOSYS_NAMESPACE  #include "VeriWrite.h"  #include "VhdlUnits.h"  #include "VeriLibrary.h" + +#if defined(YOSYSHQ_VERIFIC_INITSTATE) || defined(YOSYSHQ_VERIFIC_TEMPLATES) || defined(YOSYSHQ_VERIFIC_FORMALAPPS)  #include "VeriExtensions.h" +#endif -#ifndef SYMBIOTIC_VERIFIC_API_VERSION -#  error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." +#ifndef YOSYSHQ_VERIFIC_API_VERSION +#  error "Only YosysHQ flavored Verific is supported. Please contact office@yosyshq.com for commercial support for Yosys+Verific."  #endif -#if SYMBIOTIC_VERIFIC_API_VERSION < 20201001 -#  error "Please update your version of Symbiotic EDA flavored Verific." +#if YOSYSHQ_VERIFIC_API_VERSION < 20210103 +#  error "Please update your version of YosysHQ flavored Verific."  #endif  #ifdef __clang__ @@ -1471,7 +1474,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se  				continue;  		} -		if (inst->Type() == PRIM_SEDA_INITSTATE) +#ifdef YOSYSHQ_VERIFIC_INITSTATE +		if (inst->Type() == PRIM_YOSYSHQ_INITSTATE)  		{  			SigBit initstate = module->Initstate(new_verific_id(inst));  			SigBit sig_o = net_map_at(inst->GetOutput()); @@ -1480,7 +1484,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se  			if (!mode_keep)  				continue;  		} - +#endif  		if (!mode_keep && verific_sva_prims.count(inst->Type())) {  			if (verific_verbose)  				log("    skipping SVA cell in non k-mode\n"); @@ -1958,9 +1962,10 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par  	for (const auto &i : parameters)  		verific_params.Insert(i.first.c_str(), i.second.c_str()); +#ifdef YOSYSHQ_VERIFIC_INITSTATE  	InitialAssertionRewriter rw;  	rw.RegisterCallBack(); - +#endif  	if (top.empty()) {  		netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params);  	} @@ -2199,7 +2204,7 @@ struct VerificPass : public Pass {  		log("\n");  		log("    verific -app <application>..\n");  		log("\n"); -		log("Execute SEDA formal application on loaded Verilog files.\n"); +		log("Execute YosysHQ formal application on loaded Verilog files.\n");  		log("\n");  		log("Application options:\n");  		log("\n"); @@ -2217,7 +2222,7 @@ struct VerificPass : public Pass {  		log("\n");  		log("Applications:\n");  		log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_FORMALAPPS)  		VerificFormalApplications vfa;  		log("%s\n",vfa.GetHelp().c_str());  #else @@ -2243,18 +2248,18 @@ struct VerificPass : public Pass {  		log("\n");  		log("Templates:\n");  		log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_TEMPLATES)  		VerificTemplateGenerator vfg;  		log("%s\n",vfg.GetHelp().c_str());  #else  		log("  WARNING: Templates only available in commercial build.\n");  		log("\n");  #endif -		log("Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"); -		log("https://www.symbioticeda.com/seda-suite\n"); +		log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n"); +		log("https://www.yosyshq.com/\n");  		log("\n"); -		log("Contact office@symbioticeda.com for free evaluation\n"); -		log("binaries of Symbiotic EDA Suite.\n"); +		log("Contact office@yosyshq.com for free evaluation\n"); +		log("binaries of YosysHQ Tabby CAD Suite.\n");  		log("\n");  	}  #ifdef YOSYS_ENABLE_VERIFIC @@ -2265,11 +2270,11 @@ struct VerificPass : public Pass {  		if (check_noverific_env())  			log_cmd_error("This version of Yosys is built without Verific support.\n"  					"\n" -					"Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" -					"https://www.symbioticeda.com/seda-suite\n" +					"Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" +					"https://www.yosyshq.com/\n"  					"\n" -					"Contact office@symbioticeda.com for free evaluation\n" -					"binaries of Symbiotic EDA Suite.\n"); +					"Contact office@yosyshq.com for free evaluation\n" +					"binaries of YosysHQ Tabby CAD Suite.\n");  		log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n"); @@ -2494,6 +2499,7 @@ struct VerificPass : public Pass {  			goto check_error;  		} +#ifdef YOSYSHQ_VERIFIC_FORMALAPPS  		if (argidx < GetSize(args) && args[argidx] == "-app")  		{  			if (!(argidx+1 < GetSize(args))) @@ -2587,7 +2593,7 @@ struct VerificPass : public Pass {  			}  			goto check_error;  		} - +#endif  		if (argidx < GetSize(args) && args[argidx] == "-pp")  		{  			const char* filename = nullptr; @@ -2630,6 +2636,7 @@ struct VerificPass : public Pass {  			goto check_error;  		} +#ifdef YOSYSHQ_VERIFIC_TEMPLATES  		if (argidx < GetSize(args) && args[argidx] == "-template")  		{  			if (!(argidx+1 < GetSize(args))) @@ -2713,7 +2720,7 @@ struct VerificPass : public Pass {  				fclose(of);  			goto check_error;  		} - +#endif  		if (GetSize(args) > argidx && args[argidx] == "-import")  		{  			std::set<Netlist*> nl_todo, nl_done; @@ -2798,9 +2805,10 @@ struct VerificPass : public Pass {  			std::set<std::string> top_mod_names; +#ifdef YOSYSHQ_VERIFIC_INITSTATE  			InitialAssertionRewriter rw;  			rw.RegisterCallBack(); - +#endif  			if (mode_all)  			{  				log("Running hier_tree::ElaborateAll().\n"); @@ -2926,11 +2934,11 @@ struct VerificPass : public Pass {  	void execute(std::vector<std::string>, RTLIL::Design *) override {  		log_cmd_error("This version of Yosys is built without Verific support.\n"  				"\n" -				"Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" -				"https://www.symbioticeda.com/seda-suite\n" +				"Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" +				"https://www.yosyshq.com/\n"  				"\n" -				"Contact office@symbioticeda.com for free evaluation\n" -				"binaries of Symbiotic EDA Suite.\n"); +				"Contact office@yosyshq.com for free evaluation\n" +				"binaries of YosysHQ Tabby CAD Suite.\n");  	}  #endif  } VerificPass; diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index ea23139e2..c451c4c20 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -321,7 +321,6 @@ struct define_body_t  define_map_t::define_map_t()  {  	add("YOSYS", "1"); -	add(formal_mode ? "FORMAL" : "SYNTHESIS", "1");  }  // We must define this destructor here (rather than relying on the default), because we need to @@ -391,13 +390,16 @@ static void input_file(std::istream &f, std::string filename)  // the argument list); false if we finished with ','.  static bool read_argument(std::string &dest)  { +	skip_spaces();  	std::vector<char> openers;  	for (;;) { -		skip_spaces();  		std::string tok = next_token(true);  		if (tok == ")") { -			if (openers.empty()) +			if (openers.empty()) { +				while (dest.size() && (dest.back() == ' ' || dest.back() == '\t')) +					dest = dest.substr(0, dest.size() - 1);  				return true; +			}  			if (openers.back() != '(')  				log_error("Mismatched brackets in macro argument: %c and %c.\n",  				          openers.back(), tok[0]); diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 2e9c9b2e2..5319a45ad 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -446,6 +446,9 @@ struct VerilogFrontend : public Frontend {  			}  			break;  		} + +		defines_map.add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); +  		extra_args(f, filename, args, argidx);  		log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str()); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 678ce6c87..7fbd2aa27 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -282,7 +282,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)  %token TOK_OR_ASSIGN TOK_XOR_ASSIGN TOK_AND_ASSIGN TOK_SUB_ASSIGN  %type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int -%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list +%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type  %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number  %type <string> type_name  %type <ast> opt_enum_init enum_type struct_type non_wire_data_type @@ -619,26 +619,19 @@ non_opt_delay:  delay:  	non_opt_delay | %empty; -wire_type: -	{ -		astbuf3 = new AstNode(AST_WIRE); -		current_wire_rand = false; -		current_wire_const = false; -	} wire_type_token_list { -		$$ = astbuf3; -		SET_RULE_LOC(@$, @2, @$); -	}; +io_wire_type: +	{ astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } +	wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness +	{ $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; -wire_type_token_list: -	wire_type_token | -	wire_type_token_list wire_type_token | -	wire_type_token_io | -	hierarchical_type_id { -		astbuf3->is_custom_type = true; -		astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); -		astbuf3->children.back()->str = *$1; -		delete $1; -	}; +non_io_wire_type: +	{ astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } +	wire_type_const_rand wire_type_token wire_type_signedness +	{ $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + +wire_type: +	io_wire_type  | +	non_io_wire_type;  wire_type_token_io:  	TOK_INPUT { @@ -652,8 +645,32 @@ wire_type_token_io:  		astbuf3->is_output = true;  	}; +wire_type_signedness: +	TOK_SIGNED   { astbuf3->is_signed = true;  } | +	TOK_UNSIGNED { astbuf3->is_signed = false; } | +	%empty; + +wire_type_const_rand: +	TOK_RAND TOK_CONST { +	    current_wire_rand = true; +	    current_wire_const = true; +	} | +	TOK_CONST { +	    current_wire_const = true; +	} | +	TOK_RAND { +	    current_wire_rand = true; +	} | +	%empty; + +opt_wire_type_token: +	wire_type_token | %empty; +  wire_type_token: -	TOK_WIRE { +	hierarchical_type_id { +		astbuf3->is_custom_type = true; +		astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); +		astbuf3->children.back()->str = *$1;  	} |  	TOK_WOR {  		astbuf3->is_wor = true; @@ -661,20 +678,27 @@ wire_type_token:  	TOK_WAND {  		astbuf3->is_wand = true;  	} | +	// wires +	TOK_WIRE { +	} | +	TOK_WIRE logic_type { +	} | +	// regs  	TOK_REG {  		astbuf3->is_reg = true;  	} | -	TOK_LOGIC { -		astbuf3->is_logic = true; +	TOK_VAR TOK_REG { +		astbuf3->is_reg = true;  	} | +	// logics  	TOK_VAR {  		astbuf3->is_logic = true;  	} | -	TOK_INTEGER { -		astbuf3->is_reg = true; -		astbuf3->range_left = 31; -		astbuf3->range_right = 0; -		astbuf3->is_signed = true; +	TOK_VAR logic_type { +		astbuf3->is_logic = true; +	} | +	logic_type { +		astbuf3->is_logic = true;  	} |  	TOK_GENVAR {  		astbuf3->type = AST_GENVAR; @@ -682,15 +706,15 @@ wire_type_token:  		astbuf3->is_signed = true;  		astbuf3->range_left = 31;  		astbuf3->range_right = 0; +	}; + +logic_type: +	TOK_LOGIC {  	} | -	TOK_SIGNED { +	TOK_INTEGER { +		astbuf3->range_left = 31; +		astbuf3->range_right = 0;  		astbuf3->is_signed = true; -	} | -	TOK_RAND { -		current_wire_rand = true; -	} | -	TOK_CONST { -		current_wire_const = true;  	};  non_opt_range: @@ -746,6 +770,7 @@ module_body:  	module_body module_body_stmt |  	/* the following line makes the generate..endgenrate keywords optional */  	module_body gen_stmt | +	module_body gen_block |  	module_body ';' |  	%empty; @@ -884,7 +909,11 @@ task_func_args:  task_func_port:  	attr wire_type range { +		bool prev_was_input = true; +		bool prev_was_output = false;  		if (albuf) { +			prev_was_input = astbuf1->is_input; +			prev_was_output = astbuf1->is_output;  			delete astbuf1;  			if (astbuf2 != NULL)  				delete astbuf2; @@ -893,6 +922,12 @@ task_func_port:  		albuf = $1;  		astbuf1 = $2;  		astbuf2 = checkRange(astbuf1, $3); +		if (!astbuf1->is_input && !astbuf1->is_output) { +			if (!sv_mode) +				frontend_verilog_yyerror("task/function argument direction missing"); +			astbuf1->is_input = prev_was_input; +			astbuf1->is_output = prev_was_output; +		}  	} wire_name |  	{  		if (!astbuf1) { @@ -1456,10 +1491,10 @@ enum_base_type: type_atom type_signing  	| %empty			{ astbuf1->is_reg = true; addRange(astbuf1); }  	; -type_atom: TOK_INTEGER		{ astbuf1->is_reg = true; addRange(astbuf1); }		// 4-state signed -	|  TOK_INT		{ astbuf1->is_reg = true; addRange(astbuf1); }		// 2-state signed -	|  TOK_SHORTINT		{ astbuf1->is_reg = true; addRange(astbuf1, 15, 0); }	// 2-state signed -	|  TOK_BYTE		{ astbuf1->is_reg = true; addRange(astbuf1,  7, 0); }	// 2-state signed +type_atom: TOK_INTEGER		{ astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1); }		// 4-state signed +	|  TOK_INT		{ astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1); }		// 2-state signed +	|  TOK_SHORTINT		{ astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1, 15, 0); }	// 2-state signed +	|  TOK_BYTE		{ astbuf1->is_reg = true; astbuf1->is_signed = true; addRange(astbuf1,  7, 0); }	// 2-state signed  	;  type_vec: TOK_REG		{ astbuf1->is_reg   = true; }		// unsigned @@ -1749,7 +1784,13 @@ wire_name:  			}  			rewriteAsMemoryNode(node, $2);  		} -		if (current_function_or_task == NULL) { +		if (current_function_or_task) { +			if (node->is_input || node->is_output) +				node->port_id = current_function_or_task_port_id++; +		} else if (ast_stack.back()->type == AST_GENBLOCK) { +			if (node->is_input || node->is_output) +				frontend_verilog_yyerror("Cannot declare module port `%s' within a generate block.", $1->c_str()); +		} else {  			if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {  				port_stubs[*$1] = ++port_counter;  			} @@ -1764,9 +1805,6 @@ wire_name:  				if (node->is_input || node->is_output)  					frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str());  			} -		} else { -			if (node->is_input || node->is_output) -				node->port_id = current_function_or_task_port_id++;  		}  		//FIXME: for some reason, TOK_ID has a location which always points to one column *after* the real last column...  		SET_AST_NODE_LOC(node, @1, @1); @@ -1793,7 +1831,7 @@ type_name: TOK_ID		// first time seen  	 ;  typedef_decl: -	TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { +	TOK_TYPEDEF non_io_wire_type range type_name range_or_multirange ';' {  		astbuf1 = $2;  		astbuf2 = checkRange(astbuf1, $3);  		if (astbuf2) @@ -2425,6 +2463,16 @@ behavioral_stmt:  		exitTypeScope();  		if ($4 != NULL && $8 != NULL && *$4 != *$8)  			frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); +		AstNode *node = ast_stack.back(); +		// In SystemVerilog, unnamed blocks with block item declarations +		// create an implicit hierarchy scope +		if (sv_mode && node->str.empty()) +		    for (const AstNode* child : node->children) +			if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER +				|| child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { +			    node->str = "$unnamed_block$" + std::to_string(autoidx++); +			    break; +			}  		SET_AST_NODE_LOC(ast_stack.back(), @2, @8);  		delete $4;  		delete $8; @@ -2439,6 +2487,7 @@ behavioral_stmt:  		ast_stack.back()->children.push_back($7);  	} ';' simple_behavioral_stmt ')' {  		AstNode *block = new AstNode(AST_BLOCK); +		block->str = "$for_loop$" + std::to_string(autoidx++);  		ast_stack.back()->children.push_back(block);  		ast_stack.push_back(block);  	} behavioral_stmt { @@ -2688,6 +2737,7 @@ single_arg:  module_gen_body:  	module_gen_body gen_stmt_or_module_body_stmt | +	module_gen_body gen_block |  	%empty;  gen_stmt_or_module_body_stmt: @@ -2713,12 +2763,7 @@ gen_stmt:  		ast_stack.back()->children.push_back(node);  		ast_stack.push_back(node);  		ast_stack.back()->children.push_back($3); -		AstNode *block = new AstNode(AST_GENBLOCK); -		ast_stack.back()->children.push_back(block); -		ast_stack.push_back(block); -	} gen_stmt_block { -		ast_stack.pop_back(); -	} opt_gen_else { +	} gen_stmt_block opt_gen_else {  		SET_AST_NODE_LOC(ast_stack.back(), @1, @7);  		ast_stack.pop_back();  	} | @@ -2731,6 +2776,18 @@ gen_stmt:  		SET_AST_NODE_LOC(ast_stack.back(), @1, @7);  		ast_stack.pop_back();  	} | +	TOK_MSG_TASKS { +		AstNode *node = new AstNode(AST_TECALL); +		node->str = *$1; +		delete $1; +		ast_stack.back()->children.push_back(node); +		ast_stack.push_back(node); +	} opt_arg_list ';'{ +		SET_AST_NODE_LOC(ast_stack.back(), @1, @3); +		ast_stack.pop_back(); +	}; + +gen_block:  	TOK_BEGIN {  		enterTypeScope();  	} opt_label { @@ -2740,22 +2797,15 @@ gen_stmt:  		ast_stack.push_back(node);  	} module_gen_body TOK_END opt_label {  		exitTypeScope(); +		if ($3 != NULL && $7 != NULL && *$3 != *$7) +			frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1);  		delete $3;  		delete $7;  		SET_AST_NODE_LOC(ast_stack.back(), @1, @7);  		ast_stack.pop_back(); -	} | -	TOK_MSG_TASKS { -		AstNode *node = new AstNode(AST_TECALL); -		node->str = *$1; -		delete $1; -		ast_stack.back()->children.push_back(node); -		ast_stack.push_back(node); -	} opt_arg_list ';'{ -		SET_AST_NODE_LOC(ast_stack.back(), @1, @3); -		ast_stack.pop_back();  	}; +// result is wrapped in a genblock only if necessary  gen_stmt_block:  	{  		AstNode *node = new AstNode(AST_GENBLOCK); @@ -2764,7 +2814,7 @@ gen_stmt_block:  	} gen_stmt_or_module_body_stmt {  		SET_AST_NODE_LOC(ast_stack.back(), @2, @2);  		ast_stack.pop_back(); -	}; +	} | gen_block;  opt_gen_else:  	TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; | 
