mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	Implement a handwritten recursive-descent RTLIL parser with minimal copying
This commit is contained in:
		
							parent
							
								
									7f550468ea
								
							
						
					
					
						commit
						ac4cb5e460
					
				
					 5 changed files with 755 additions and 773 deletions
				
			
		|  | @ -1,19 +1 @@ | |||
| 
 | ||||
| GENFILES += frontends/rtlil/rtlil_parser.tab.cc | ||||
| GENFILES += frontends/rtlil/rtlil_parser.tab.hh | ||||
| GENFILES += frontends/rtlil/rtlil_parser.output | ||||
| GENFILES += frontends/rtlil/rtlil_lexer.cc | ||||
| 
 | ||||
| frontends/rtlil/rtlil_parser.tab.cc: frontends/rtlil/rtlil_parser.y | ||||
| 	$(Q) mkdir -p $(dir $@) | ||||
| 	$(P) $(BISON) -o $@ -d -r all -b frontends/rtlil/rtlil_parser $< | ||||
| 
 | ||||
| frontends/rtlil/rtlil_parser.tab.hh: frontends/rtlil/rtlil_parser.tab.cc | ||||
| 
 | ||||
| frontends/rtlil/rtlil_lexer.cc: frontends/rtlil/rtlil_lexer.l | ||||
| 	$(Q) mkdir -p $(dir $@) | ||||
| 	$(P) flex -o frontends/rtlil/rtlil_lexer.cc $< | ||||
| 
 | ||||
| OBJS += frontends/rtlil/rtlil_parser.tab.o frontends/rtlil/rtlil_lexer.o | ||||
| OBJS += frontends/rtlil/rtlil_frontend.o | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,27 +17,762 @@ | |||
|  * | ||||
|  *  --- | ||||
|  * | ||||
|  *  A very simple and straightforward frontend for the RTLIL text | ||||
|  *  representation. | ||||
|  *  A handwritten recursive-descent parser for the RTLIL text representation. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "rtlil_frontend.h" | ||||
| #include "kernel/register.h" | ||||
| #include "kernel/log.h" | ||||
| 
 | ||||
| void rtlil_frontend_yyerror(char const *s) | ||||
| { | ||||
| 	YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s); | ||||
| } | ||||
| 
 | ||||
| void rtlil_frontend_yywarning(char const *s) | ||||
| { | ||||
| 	YOSYS_NAMESPACE_PREFIX log_warning("In line %d: %s\n", rtlil_frontend_yyget_lineno(), s); | ||||
| } | ||||
| #include "kernel/utils.h" | ||||
| #include <charconv> | ||||
| #include <deque> | ||||
| #include <optional> | ||||
| 
 | ||||
| YOSYS_NAMESPACE_BEGIN | ||||
| 
 | ||||
| struct RTLILFrontendWorker { | ||||
| 	std::istream *f = nullptr; | ||||
| 	RTLIL::Design *design; | ||||
| 	bool flag_nooverwrite = false; | ||||
| 	bool flag_overwrite = false; | ||||
| 	bool flag_lib = false; | ||||
| 
 | ||||
| 	int line_num; | ||||
| 	std::string line_buf; | ||||
| 	// Substring of line_buf. Always newline-terminated, thus never empty.
 | ||||
| 	std::string_view line; | ||||
| 
 | ||||
| 	RTLIL::Module *current_module; | ||||
| 	dict<RTLIL::IdString, RTLIL::Const> attrbuf; | ||||
| 	std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack; | ||||
| 	std::vector<RTLIL::CaseRule*> case_stack; | ||||
| 
 | ||||
| 	template <typename... Args> | ||||
| 	[[noreturn]] | ||||
| 	void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args) | ||||
| 	{ | ||||
| 		log_error("Parser error in line %d: %s\n", line_num, fmt.format(args...)); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename... Args> | ||||
| 	void warning(FmtString<TypeIdentity<Args>...> fmt, const Args &... args) | ||||
| 	{ | ||||
| 		log_warning("In line %d: %s\n", line_num, fmt.format(args...)); | ||||
| 	} | ||||
| 
 | ||||
| 	// May return an empty line if the stream is not good().
 | ||||
| 	void advance_to_next_nonempty_line() | ||||
| 	{ | ||||
| 		if (!f->good()) { | ||||
| 			line = "\n"; | ||||
| 			return; | ||||
| 		} | ||||
| 		while (true) { | ||||
| 			std::getline(*f, line_buf); | ||||
| 			line_num++; | ||||
| 			if (line_buf.empty() || line_buf[line_buf.size() - 1] != '\n') | ||||
| 				line_buf += '\n'; | ||||
| 			line = line_buf; | ||||
| 			consume_whitespace_and_comments(); | ||||
| 			if (line[0] != '\n' || !f->good()) | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void consume_whitespace_and_comments() | ||||
| 	{ | ||||
| 		while (true) { | ||||
| 			switch (line[0]) { | ||||
| 			case ' ': | ||||
| 			case '\t': | ||||
| 				line = line.substr(1); | ||||
| 				break; | ||||
| 			case '#': | ||||
| 				line = "\n"; | ||||
| 				return; | ||||
| 			default: | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	bool try_parse_keyword(std::string_view keyword) | ||||
| 	{ | ||||
| 		int keyword_size = keyword.size(); | ||||
| 		if (keyword != line.substr(0, keyword_size)) | ||||
| 			return false; | ||||
| 		// This index is safe because `line` is always newline-terminated
 | ||||
| 		// and `keyword` never contains a newline.
 | ||||
| 		char ch = line[keyword_size]; | ||||
| 		if (ch >= 'a' && ch <= 'z') | ||||
| 			return false; | ||||
| 		line = line.substr(keyword_size); | ||||
| 		consume_whitespace_and_comments(); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string error_token() | ||||
| 	{ | ||||
| 		std::string result; | ||||
| 		for (char ch : line) { | ||||
| 			if (ch == '\n' || ch == ' ' || ch == '\t') | ||||
| 				break; | ||||
| 			result += ch; | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	void expect_keyword(std::string_view keyword) | ||||
| 	{ | ||||
| 		if (!try_parse_keyword(keyword)) | ||||
| 			error("Expected token `%s', got `%s'.", keyword, error_token()); | ||||
| 	} | ||||
| 
 | ||||
| 	bool try_parse_char(char ch) | ||||
| 	{ | ||||
| 		if (line[0] != ch) | ||||
| 			return false; | ||||
| 		line = line.substr(1); | ||||
| 		consume_whitespace_and_comments(); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	void expect_char(char ch) | ||||
| 	{ | ||||
| 		if (!try_parse_char(ch)) | ||||
| 			error("Expected `%c', got `%s'.", ch, error_token()); | ||||
| 	} | ||||
| 
 | ||||
| 	bool try_parse_eol() | ||||
| 	{ | ||||
| 		if (line[0] != '\n') | ||||
| 			return false; | ||||
| 		advance_to_next_nonempty_line(); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	void expect_eol() | ||||
| 	{ | ||||
| 		if (!try_parse_eol()) | ||||
| 			error("Expected EOL, got `%s'.", error_token()); | ||||
| 	} | ||||
| 
 | ||||
| 	std::optional<RTLIL::IdString> try_parse_id() | ||||
| 	{ | ||||
| 		char ch = line[0]; | ||||
| 		if (ch != '\\' && ch != '$') | ||||
| 			return std::nullopt; | ||||
| 		int idx = 1; | ||||
| 		while (true) { | ||||
| 			ch = line[idx]; | ||||
| 			if (ch <= ' ' && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) | ||||
| 				break; | ||||
| 			++idx; | ||||
| 		} | ||||
| 		IdString result(line.substr(0, idx)); | ||||
| 		line = line.substr(idx); | ||||
| 		consume_whitespace_and_comments(); | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	RTLIL::IdString parse_id() | ||||
| 	{ | ||||
| 		std::optional<RTLIL::IdString> id = try_parse_id(); | ||||
| 		if (!id.has_value()) | ||||
| 			error("Expected ID, got `%s'.", error_token()); | ||||
| 		return std::move(*id); | ||||
| 	} | ||||
| 
 | ||||
| 	long long parse_integer() | ||||
| 	{ | ||||
| 		long long result = parse_integer_alone(); | ||||
| 		consume_whitespace_and_comments(); | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	long long parse_integer_alone() | ||||
| 	{ | ||||
| 		int idx = 0; | ||||
| 		if (line[idx] == '-') | ||||
| 			++idx; | ||||
| 		while (true) { | ||||
| 			char ch = line[idx]; | ||||
| 			if (ch < '0' || ch > '9') | ||||
| 				break; | ||||
| 			++idx; | ||||
| 		} | ||||
| 		long long result; | ||||
| 		if (std::from_chars(line.data(), line.data() + idx, result, 10).ec != std::errc{}) | ||||
| 			error("Invalid integer `%s'.", error_token()); | ||||
| 		line = line.substr(idx); | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string parse_string() | ||||
| 	{ | ||||
| 		if (line[0] != '\"') | ||||
| 			error("Expected string, got `%s'.", error_token()); | ||||
| 		std::string str; | ||||
| 		int idx = 1; | ||||
| 		while (true) { | ||||
| 			int start_idx = idx; | ||||
| 			char ch; | ||||
| 			while (true) { | ||||
| 				ch = line[idx]; | ||||
| 				if (ch == '"' || ch == '\n' || ch == '\\' || ch == 0) | ||||
| 					break; | ||||
| 				++idx; | ||||
| 			} | ||||
| 			str.append(line.data() + start_idx, line.data() + idx); | ||||
| 			++idx; | ||||
| 			if (ch == '"') | ||||
| 				break; | ||||
| 			if (ch == 0) | ||||
| 				error("Null byte in string literal: `%s'.", line); | ||||
| 			if (ch == '\n') | ||||
| 				error("Unterminated string literal: `%s'.", line); | ||||
| 			ch = line[idx++]; | ||||
| 			if (ch == 'n') { | ||||
| 				ch = '\n'; | ||||
| 			} else if (ch == 't') { | ||||
| 				ch = '\t'; | ||||
| 			} else if (ch >= '0' && ch <= '7') { | ||||
| 				int v = ch - '0'; | ||||
| 				char next_ch = line[idx + 1]; | ||||
| 				if (next_ch >= '0' && next_ch <= '7') { | ||||
| 					++idx; | ||||
| 					v = v*8 + (next_ch - '0'); | ||||
| 					next_ch = line[idx + 1]; | ||||
| 					if (next_ch >= '0' && next_ch <= '7') { | ||||
| 						++idx; | ||||
| 						v = v*8 + (next_ch - '0'); | ||||
| 					} | ||||
| 				} | ||||
| 				ch = v; | ||||
| 			} | ||||
| 			str += ch; | ||||
| 		} | ||||
| 		line = line.substr(idx); | ||||
| 		consume_whitespace_and_comments(); | ||||
| 		return str; | ||||
| 	} | ||||
| 
 | ||||
| 	RTLIL::Const parse_const() | ||||
| 	{ | ||||
| 		if (line[0] == '"') | ||||
| 			return RTLIL::Const(parse_string()); | ||||
| 
 | ||||
| 		bool negative_value = line[0] == '-'; | ||||
| 		long long width = parse_integer_alone(); | ||||
| 		// Can't test value<0 here because we need to stop parsing after '-0'
 | ||||
| 		if (negative_value || line[0] != '\'') { | ||||
| 			if (width < INT_MIN || width > INT_MAX) | ||||
| 				error("Integer %lld out of range in `%s'.", width, error_token()); | ||||
| 			consume_whitespace_and_comments(); | ||||
| 			return RTLIL::Const(width); | ||||
| 		} | ||||
| 
 | ||||
| 		int idx = 1; | ||||
| 		bool is_signed = line[1] == 's'; | ||||
| 		if (is_signed) | ||||
| 			++idx; | ||||
| 
 | ||||
| 		std::vector<RTLIL::State> bits; | ||||
| 		bits.reserve(width); | ||||
| 		while (true) { | ||||
| 			RTLIL::State bit; | ||||
| 			switch (line[idx]) { | ||||
| 			case '0': bit = RTLIL::S0; break; | ||||
| 			case '1': bit = RTLIL::S1; break; | ||||
| 			case 'x': bit = RTLIL::Sx; break; | ||||
| 			case 'z': bit = RTLIL::Sz; break; | ||||
| 			case 'm': bit = RTLIL::Sm; break; | ||||
| 			case '-': bit = RTLIL::Sa; break; | ||||
| 			default: goto done; | ||||
| 			} | ||||
| 			bits.push_back(bit); | ||||
| 			++idx; | ||||
| 		} | ||||
| 		done: | ||||
| 		std::reverse(bits.begin(), bits.end()); | ||||
| 
 | ||||
| 		if (GetSize(bits) > width) | ||||
| 			bits.resize(width); | ||||
| 		else if (GetSize(bits) < width) { | ||||
| 			RTLIL::State extbit = RTLIL::Sx; | ||||
| 			if (!bits.empty()) { | ||||
| 				extbit = bits.back(); | ||||
| 				if (extbit == RTLIL::S1) | ||||
| 					extbit = RTLIL::S0; | ||||
| 			} | ||||
| 			bits.resize(width, extbit); | ||||
| 		} | ||||
| 
 | ||||
| 		RTLIL::Const val(std::move(bits)); | ||||
| 		if (is_signed) | ||||
| 			val.flags |= RTLIL::CONST_FLAG_SIGNED; | ||||
| 		line = line.substr(idx); | ||||
| 		consume_whitespace_and_comments(); | ||||
| 		return val; | ||||
| 	} | ||||
| 
 | ||||
| 	RTLIL::SigSpec parse_sigspec() | ||||
| 	{ | ||||
| 		if (try_parse_char('{')) { | ||||
| 			std::vector<SigSpec> parts; | ||||
| 			while (!try_parse_char('}')) | ||||
| 				parts.push_back(parse_sigspec()); | ||||
| 			RTLIL::SigSpec sig; | ||||
| 			for (auto it = parts.rbegin(); it != parts.rend(); ++it) | ||||
| 				sig.append(std::move(*it)); | ||||
| 			return sig; | ||||
| 		} | ||||
| 
 | ||||
| 		RTLIL::SigSpec sig; | ||||
| 
 | ||||
| 		// We could add a special path for parsing IdStrings that must already exist,
 | ||||
| 		// as here.
 | ||||
| 		// We don't need to addref/release in this case.
 | ||||
| 		std::optional<RTLIL::IdString> id = try_parse_id(); | ||||
| 		if (id.has_value()) { | ||||
| 			RTLIL::Wire *wire = current_module->wire(*id); | ||||
| 			if (wire == nullptr) | ||||
| 				error("Wire `%s' not found.", *id); | ||||
| 			sig = RTLIL::SigSpec(wire); | ||||
| 		} else { | ||||
| 			sig = RTLIL::SigSpec(parse_const()); | ||||
| 		} | ||||
| 
 | ||||
| 		while (try_parse_char('[')) { | ||||
| 			int left = parse_integer(); | ||||
| 			if (left >= sig.size() || left < 0) | ||||
| 				error("bit index %d out of range", left); | ||||
| 			if (try_parse_char(':')) { | ||||
| 				int right = parse_integer(); | ||||
| 				if (right < 0) | ||||
| 					error("bit index %d out of range", right); | ||||
| 				if (left < right) | ||||
| 					error("invalid slice [%d:%d]", left, right); | ||||
| 				sig = sig.extract(right, left-right+1); | ||||
| 			} else { | ||||
| 				sig = sig.extract(left); | ||||
| 			} | ||||
| 			expect_char(']'); | ||||
| 		} | ||||
| 
 | ||||
| 		return sig; | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_module() | ||||
| 	{ | ||||
| 		RTLIL::IdString module_name = parse_id(); | ||||
| 		expect_eol(); | ||||
| 
 | ||||
| 		bool delete_current_module = false; | ||||
| 		if (design->has(module_name)) { | ||||
| 			RTLIL::Module *existing_mod = design->module(module_name); | ||||
| 			if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) { | ||||
| 				log("Ignoring blackbox re-definition of module %s.\n", module_name); | ||||
| 				delete_current_module = true; | ||||
| 			} else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { | ||||
| 				error("RTLIL error: redefinition of module %s.", module_name); | ||||
| 			} else if (flag_nooverwrite) { | ||||
| 				log("Ignoring re-definition of module %s.\n", module_name); | ||||
| 				delete_current_module = true; | ||||
| 			} else { | ||||
| 				log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", module_name); | ||||
| 				design->remove(existing_mod); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		current_module = new RTLIL::Module; | ||||
| 		current_module->name = std::move(module_name); | ||||
| 		current_module->attributes = std::move(attrbuf); | ||||
| 		if (!delete_current_module) | ||||
| 			design->add(current_module); | ||||
| 
 | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			if (try_parse_keyword("attribute")) { | ||||
| 				parse_attribute(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("parameter")) { | ||||
| 				parse_parameter(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("connect")) { | ||||
| 				parse_connect(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("wire")) { | ||||
| 				parse_wire(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("cell")) { | ||||
| 				parse_cell(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("memory")) { | ||||
| 				parse_memory(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("process")) { | ||||
| 				parse_process(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("end")) { | ||||
| 				expect_eol(); | ||||
| 				break; | ||||
| 			} | ||||
| 			error("Unexpected token in module body: %s", error_token()); | ||||
| 		} | ||||
| 
 | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			error("dangling attribute"); | ||||
| 		current_module->fixup_ports(); | ||||
| 		if (delete_current_module) | ||||
| 			delete current_module; | ||||
| 		else if (flag_lib) | ||||
| 			current_module->makeblackbox(); | ||||
| 		current_module = nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_attribute() | ||||
| 	{ | ||||
| 		RTLIL::IdString id = parse_id(); | ||||
| 		RTLIL::Const c = parse_const(); | ||||
| 		attrbuf.insert({std::move(id), std::move(c)}); | ||||
| 		expect_eol(); | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_parameter() | ||||
| 	{ | ||||
| 		RTLIL::IdString id = parse_id(); | ||||
| 		current_module->avail_parameters(id); | ||||
| 		if (try_parse_eol()) | ||||
| 			return; | ||||
| 		RTLIL::Const c = parse_const(); | ||||
| 		current_module->parameter_default_values.insert({std::move(id), std::move(c)}); | ||||
| 		expect_eol(); | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_wire() | ||||
| 	{ | ||||
| 		RTLIL::Wire *wire; | ||||
| 		int width = 1; | ||||
| 		int start_offset = 0; | ||||
| 		int port_id = 0; | ||||
| 		bool port_input = false; | ||||
| 		bool port_output = false; | ||||
| 		bool upto = false; | ||||
| 		bool is_signed = false; | ||||
| 
 | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			std::optional<RTLIL::IdString> id = try_parse_id(); | ||||
| 			if (id.has_value()) { | ||||
| 				if (current_module->wire(*id) != nullptr) | ||||
| 					error("RTLIL error: redefinition of wire %s.", *id); | ||||
| 				wire = current_module->addWire(std::move(*id)); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (try_parse_keyword("width")) | ||||
| 				width = parse_integer(); | ||||
| 			else if (try_parse_keyword("upto")) | ||||
| 				upto = true; | ||||
| 			else if (try_parse_keyword("signed")) | ||||
| 				is_signed = true; | ||||
| 			else if (try_parse_keyword("offset")) | ||||
| 				start_offset = parse_integer(); | ||||
| 			else if (try_parse_keyword("input")) { | ||||
| 				port_id = parse_integer(); | ||||
| 				port_input = true; | ||||
| 			} else if (try_parse_keyword("output")) { | ||||
| 				port_id = parse_integer(); | ||||
| 				port_output = true; | ||||
| 			} else if (try_parse_keyword("inout")) { | ||||
| 				port_id = parse_integer(); | ||||
| 				port_input = true; | ||||
| 				port_output = true; | ||||
| 			} else if (try_parse_eol()) | ||||
| 				error("Missing wire ID"); | ||||
| 			else | ||||
| 				error("Unexpected wire option: %s", error_token()); | ||||
| 		} | ||||
| 
 | ||||
| 		wire->attributes = std::move(attrbuf); | ||||
| 		wire->width = width; | ||||
| 		wire->upto = upto; | ||||
| 		wire->start_offset = start_offset; | ||||
| 		wire->is_signed = is_signed; | ||||
| 		wire->port_id = port_id; | ||||
| 		wire->port_input = port_input; | ||||
| 		wire->port_output = port_output; | ||||
| 		expect_eol(); | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_memory() | ||||
| 	{ | ||||
| 		RTLIL::Memory *memory = new RTLIL::Memory; | ||||
| 		memory->attributes = std::move(attrbuf); | ||||
| 
 | ||||
| 		int width = 1; | ||||
| 		int start_offset = 0; | ||||
| 		int size = 0; | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			std::optional<RTLIL::IdString> id = try_parse_id(); | ||||
| 			if (id.has_value()) { | ||||
| 				if (current_module->memories.count(*id) != 0) | ||||
| 					error("RTLIL error: redefinition of memory %s.", *id); | ||||
| 				memory->name = std::move(*id); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (try_parse_keyword("width")) | ||||
| 				width = parse_integer(); | ||||
| 			else if (try_parse_keyword("size")) | ||||
| 				size = parse_integer(); | ||||
| 			else if (try_parse_keyword("offset")) | ||||
| 				start_offset = parse_integer(); | ||||
| 			else if (try_parse_eol()) | ||||
| 				error("Missing memory ID"); | ||||
| 			else | ||||
| 				error("Unexpected memory option: %s", error_token()); | ||||
| 		} | ||||
| 		memory->width = width; | ||||
| 		memory->start_offset = start_offset; | ||||
| 		memory->size = size; | ||||
| 		current_module->memories.insert({memory->name, memory}); | ||||
| 		expect_eol(); | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_cell() | ||||
| 	{ | ||||
| 		RTLIL::IdString cell_type = parse_id(); | ||||
| 		RTLIL::IdString cell_name = parse_id(); | ||||
| 		expect_eol(); | ||||
| 
 | ||||
| 		if (current_module->cell(cell_name) != nullptr) | ||||
| 			error("RTLIL error: redefinition of cell %s.", cell_name); | ||||
| 		RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type); | ||||
| 		cell->attributes = std::move(attrbuf); | ||||
| 
 | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			if (try_parse_keyword("parameter")) { | ||||
| 				bool is_signed = false; | ||||
| 				bool is_real = false; | ||||
| 				if (try_parse_keyword("signed")) { | ||||
| 					is_signed = true; | ||||
| 				} else if (try_parse_keyword("real")) { | ||||
| 					is_real = true; | ||||
| 				} | ||||
| 				RTLIL::IdString param_name = parse_id(); | ||||
| 				RTLIL::Const val = parse_const(); | ||||
| 				if (is_signed) | ||||
| 					val.flags |= RTLIL::CONST_FLAG_SIGNED; | ||||
| 				if (is_real) | ||||
| 					val.flags |= RTLIL::CONST_FLAG_REAL; | ||||
| 				cell->parameters.insert({std::move(param_name), std::move(val)}); | ||||
| 				expect_eol(); | ||||
| 			} else if (try_parse_keyword("connect")) { | ||||
| 				RTLIL::IdString port_name = parse_id(); | ||||
| 				if (cell->hasPort(port_name)) | ||||
| 					error("RTLIL error: redefinition of cell port %s.", port_name); | ||||
| 				cell->setPort(std::move(port_name), parse_sigspec()); | ||||
| 				expect_eol(); | ||||
| 			} else if (try_parse_keyword("end")) { | ||||
| 				expect_eol(); | ||||
| 				break; | ||||
| 			} else { | ||||
| 				error("Unexpected token in cell body: %s", error_token()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_connect() | ||||
| 	{ | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			error("dangling attribute"); | ||||
| 		RTLIL::SigSpec s1 = parse_sigspec(); | ||||
| 		RTLIL::SigSpec s2 = parse_sigspec(); | ||||
| 		current_module->connect(std::move(s1), std::move(s2)); | ||||
| 		expect_eol(); | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_case_body(RTLIL::CaseRule *current_case) | ||||
| 	{ | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			if (try_parse_keyword("attribute")) | ||||
| 				parse_attribute(); | ||||
| 			else if (try_parse_keyword("switch")) | ||||
| 				parse_switch(); | ||||
| 			else if (try_parse_keyword("assign")) { | ||||
| 				if (attrbuf.size() != 0) | ||||
| 					error("dangling attribute"); | ||||
| 				// See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this
 | ||||
| 				// warning
 | ||||
| 				if (!switch_stack.back()->empty()) | ||||
| 					warning("case rule assign statements after switch statements may cause unexpected behaviour. " | ||||
| 						"The assign statement is reordered to come before all switch statements."); | ||||
| 				RTLIL::SigSpec s1 = parse_sigspec(); | ||||
| 				RTLIL::SigSpec s2 = parse_sigspec(); | ||||
| 				current_case->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2))); | ||||
| 				expect_eol(); | ||||
| 			} else | ||||
| 				return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_switch() | ||||
| 	{ | ||||
| 		RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; | ||||
| 		rule->signal = parse_sigspec(); | ||||
| 		rule->attributes = std::move(attrbuf); | ||||
| 		switch_stack.back()->push_back(rule); | ||||
| 		expect_eol(); | ||||
| 
 | ||||
| 		while (true) { | ||||
| 			if (try_parse_keyword("attribute")) { | ||||
| 				parse_attribute(); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if (try_parse_keyword("end")) { | ||||
| 				expect_eol(); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			expect_keyword("case"); | ||||
| 			RTLIL::CaseRule *case_rule = new RTLIL::CaseRule; | ||||
| 			case_rule->attributes = std::move(attrbuf); | ||||
| 			rule->cases.push_back(case_rule); | ||||
| 			switch_stack.push_back(&case_rule->switches); | ||||
| 			case_stack.push_back(case_rule); | ||||
| 
 | ||||
| 			if (!try_parse_eol()) { | ||||
| 				while (true) { | ||||
| 					case_rule->compare.push_back(parse_sigspec()); | ||||
| 					if (try_parse_eol()) | ||||
| 						break; | ||||
| 					expect_char(','); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			parse_case_body(case_rule); | ||||
| 
 | ||||
| 			switch_stack.pop_back(); | ||||
| 			case_stack.pop_back(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void parse_process() | ||||
| 	{ | ||||
| 		RTLIL::IdString proc_name = parse_id(); | ||||
| 		expect_eol(); | ||||
| 
 | ||||
| 		if (current_module->processes.count(proc_name) != 0) | ||||
| 			error("RTLIL error: redefinition of process %s.", proc_name); | ||||
| 		RTLIL::Process *proc = current_module->addProcess(std::move(proc_name)); | ||||
| 		proc->attributes = std::move(attrbuf); | ||||
| 
 | ||||
| 		switch_stack.clear(); | ||||
| 		switch_stack.push_back(&proc->root_case.switches); | ||||
| 		case_stack.clear(); | ||||
| 		case_stack.push_back(&proc->root_case); | ||||
| 
 | ||||
| 		parse_case_body(&proc->root_case); | ||||
| 
 | ||||
| 		while (try_parse_keyword("sync")) | ||||
| 		{ | ||||
| 			RTLIL::SyncRule *rule = new RTLIL::SyncRule; | ||||
| 
 | ||||
| 			if (try_parse_keyword("low")) rule->type = RTLIL::ST0; | ||||
| 			else if (try_parse_keyword("high")) rule->type = RTLIL::ST1; | ||||
| 			else if (try_parse_keyword("posedge")) rule->type = RTLIL::STp; | ||||
| 			else if (try_parse_keyword("negedge")) rule->type = RTLIL::STn; | ||||
| 			else if (try_parse_keyword("edge")) rule->type = RTLIL::STe; | ||||
| 			else if (try_parse_keyword("always")) rule->type = RTLIL::STa; | ||||
| 			else if (try_parse_keyword("global")) rule->type = RTLIL::STg; | ||||
| 			else if (try_parse_keyword("init")) rule->type = RTLIL::STi; | ||||
| 			else error("Unexpected sync type: %s", error_token()); | ||||
| 
 | ||||
| 			if (rule->type != RTLIL::STa && rule->type != RTLIL::STg && rule->type != RTLIL::STi) | ||||
| 				rule->signal = parse_sigspec(); | ||||
| 			proc->syncs.push_back(rule); | ||||
| 			expect_eol(); | ||||
| 
 | ||||
| 			bool attributes_in_update_list = false; | ||||
| 			while (true) | ||||
| 			{ | ||||
| 				if (try_parse_keyword("update")) { | ||||
| 					RTLIL::SigSpec s1 = parse_sigspec(); | ||||
| 					RTLIL::SigSpec s2 = parse_sigspec(); | ||||
| 					rule->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2))); | ||||
| 					expect_eol(); | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
| 				if (try_parse_keyword("attribute")) { | ||||
| 					attributes_in_update_list = true; | ||||
| 					parse_attribute(); | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
| 				if (!try_parse_keyword("memwr")) | ||||
| 					break; | ||||
| 
 | ||||
| 				RTLIL::MemWriteAction act; | ||||
| 				act.attributes = std::move(attrbuf); | ||||
| 				act.memid = parse_id(); | ||||
| 				act.address = parse_sigspec(); | ||||
| 				act.data = parse_sigspec(); | ||||
| 				act.enable = parse_sigspec(); | ||||
| 				act.priority_mask = parse_const(); | ||||
| 				rule->mem_write_actions.push_back(std::move(act)); | ||||
| 				expect_eol(); | ||||
| 			} | ||||
| 			// The old parser allowed dangling attributes before a "sync" to carry through
 | ||||
| 			// the "sync", so we will too, for now.
 | ||||
| 			if (attributes_in_update_list && attrbuf.size() > 0) | ||||
| 				error("dangling attribute"); | ||||
| 		} | ||||
| 
 | ||||
| 		expect_keyword("end"); | ||||
| 		expect_eol(); | ||||
| 	} | ||||
| 
 | ||||
| 	RTLILFrontendWorker(RTLIL::Design *design) : design(design) {} | ||||
| 
 | ||||
| 	void parse(std::istream *f) | ||||
| 	{ | ||||
| 		this->f = f; | ||||
| 		line_num = 0; | ||||
| 		advance_to_next_nonempty_line(); | ||||
| 		while (f->good()) | ||||
| 		{ | ||||
| 			if (try_parse_keyword("attribute")) { | ||||
| 				parse_attribute(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("module")) { | ||||
| 				parse_module(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (try_parse_keyword("autoidx")) { | ||||
| 				autoidx = std::max<int>(autoidx, parse_integer()); | ||||
| 				expect_eol(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			error("Unexpected token: %s", error_token()); | ||||
| 		} | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			error("dangling attribute"); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct RTLILFrontend : public Frontend { | ||||
| 	RTLILFrontend() : Frontend("rtlil", "read modules from RTLIL file") { } | ||||
| 	void help() override | ||||
|  | @ -63,9 +798,7 @@ struct RTLILFrontend : public Frontend { | |||
| 	} | ||||
| 	void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override | ||||
| 	{ | ||||
| 		RTLIL_FRONTEND::flag_nooverwrite = false; | ||||
| 		RTLIL_FRONTEND::flag_overwrite = false; | ||||
| 		RTLIL_FRONTEND::flag_lib = false; | ||||
| 		RTLILFrontendWorker worker(design); | ||||
| 
 | ||||
| 		log_header(design, "Executing RTLIL frontend.\n"); | ||||
| 
 | ||||
|  | @ -73,17 +806,17 @@ struct RTLILFrontend : public Frontend { | |||
| 		for (argidx = 1; argidx < args.size(); argidx++) { | ||||
| 			std::string arg = args[argidx]; | ||||
| 			if (arg == "-nooverwrite") { | ||||
| 				RTLIL_FRONTEND::flag_nooverwrite = true; | ||||
| 				RTLIL_FRONTEND::flag_overwrite = false; | ||||
| 				worker.flag_nooverwrite = true; | ||||
| 				worker.flag_overwrite = false; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (arg == "-overwrite") { | ||||
| 				RTLIL_FRONTEND::flag_nooverwrite = false; | ||||
| 				RTLIL_FRONTEND::flag_overwrite = true; | ||||
| 				worker.flag_nooverwrite = false; | ||||
| 				worker.flag_overwrite = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (arg == "-lib") { | ||||
| 				RTLIL_FRONTEND::flag_lib = true; | ||||
| 				worker.flag_lib = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
|  | @ -92,14 +825,8 @@ struct RTLILFrontend : public Frontend { | |||
| 
 | ||||
| 		log("Input filename: %s\n", filename); | ||||
| 
 | ||||
| 		RTLIL_FRONTEND::lexin = f; | ||||
| 		RTLIL_FRONTEND::current_design = design; | ||||
| 		rtlil_frontend_yydebug = false; | ||||
| 		rtlil_frontend_yyrestart(NULL); | ||||
| 		rtlil_frontend_yyparse(); | ||||
| 		rtlil_frontend_yylex_destroy(); | ||||
| 		worker.parse(f); | ||||
| 	} | ||||
| } RTLILFrontend; | ||||
| 
 | ||||
| YOSYS_NAMESPACE_END | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,52 +0,0 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted, provided that the above | ||||
|  *  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. | ||||
|  * | ||||
|  *  --- | ||||
|  * | ||||
|  *  A very simple and straightforward frontend for the RTLIL text | ||||
|  *  representation. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef RTLIL_FRONTEND_H | ||||
| #define RTLIL_FRONTEND_H | ||||
| 
 | ||||
| #include "kernel/yosys.h" | ||||
| 
 | ||||
| YOSYS_NAMESPACE_BEGIN | ||||
| 
 | ||||
| namespace RTLIL_FRONTEND { | ||||
| 	extern std::istream *lexin; | ||||
| 	extern RTLIL::Design *current_design; | ||||
| 	extern bool flag_nooverwrite; | ||||
| 	extern bool flag_overwrite; | ||||
| 	extern bool flag_lib; | ||||
| } | ||||
| 
 | ||||
| YOSYS_NAMESPACE_END | ||||
| 
 | ||||
| extern int rtlil_frontend_yydebug; | ||||
| int rtlil_frontend_yylex(void); | ||||
| void rtlil_frontend_yyerror(char const *s); | ||||
| void rtlil_frontend_yywarning(char const *s); | ||||
| void rtlil_frontend_yyrestart(FILE *f); | ||||
| int rtlil_frontend_yyparse(void); | ||||
| int rtlil_frontend_yylex_destroy(void); | ||||
| int rtlil_frontend_yyget_lineno(void); | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | @ -1,150 +0,0 @@ | |||
| /* | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted, provided that the above | ||||
|  *  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. | ||||
|  * | ||||
|  *  --- | ||||
|  * | ||||
|  *  A very simple and straightforward frontend for the RTLIL text | ||||
|  *  representation. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| %{ | ||||
| 
 | ||||
| #ifdef __clang__ | ||||
| // bison generates code using the 'register' storage class specifier | ||||
| #pragma clang diagnostic ignored "-Wdeprecated-register" | ||||
| #endif | ||||
| 
 | ||||
| #include <cstdlib> | ||||
| #include "frontends/rtlil/rtlil_frontend.h" | ||||
| #include "rtlil_parser.tab.hh" | ||||
| 
 | ||||
| USING_YOSYS_NAMESPACE | ||||
| 
 | ||||
| #define YY_INPUT(buf,result,max_size) \ | ||||
| 	result = readsome(*RTLIL_FRONTEND::lexin, buf, max_size) | ||||
| 
 | ||||
| %} | ||||
| 
 | ||||
| %option yylineno | ||||
| %option noyywrap | ||||
| %option nounput | ||||
| %option prefix="rtlil_frontend_yy" | ||||
| 
 | ||||
| %x STRING | ||||
| 
 | ||||
| %% | ||||
| 
 | ||||
| "autoidx"	{ return TOK_AUTOIDX; } | ||||
| "module"	{ return TOK_MODULE; } | ||||
| "attribute"	{ return TOK_ATTRIBUTE; } | ||||
| "parameter"	{ return TOK_PARAMETER; } | ||||
| "signed"	{ return TOK_SIGNED; } | ||||
| "real"		{ return TOK_REAL; } | ||||
| "wire"		{ return TOK_WIRE; } | ||||
| "memory"	{ return TOK_MEMORY; } | ||||
| "width"		{ return TOK_WIDTH; } | ||||
| "upto"		{ return TOK_UPTO; } | ||||
| "offset"	{ return TOK_OFFSET; } | ||||
| "size"		{ return TOK_SIZE; } | ||||
| "input"		{ return TOK_INPUT; } | ||||
| "output"	{ return TOK_OUTPUT; } | ||||
| "inout"		{ return TOK_INOUT; } | ||||
| "cell"		{ return TOK_CELL; } | ||||
| "connect"	{ return TOK_CONNECT; } | ||||
| "switch"	{ return TOK_SWITCH; } | ||||
| "case"		{ return TOK_CASE; } | ||||
| "assign"	{ return TOK_ASSIGN; } | ||||
| "sync"		{ return TOK_SYNC; } | ||||
| "low"		{ return TOK_LOW; } | ||||
| "high"		{ return TOK_HIGH; } | ||||
| "posedge"	{ return TOK_POSEDGE; } | ||||
| "negedge"	{ return TOK_NEGEDGE; } | ||||
| "edge"		{ return TOK_EDGE; } | ||||
| "always"	{ return TOK_ALWAYS; } | ||||
| "global"	{ return TOK_GLOBAL; } | ||||
| "init"		{ return TOK_INIT; } | ||||
| "update"	{ return TOK_UPDATE; } | ||||
| "memwr"		{ return TOK_MEMWR; } | ||||
| "process"	{ return TOK_PROCESS; } | ||||
| "end"		{ return TOK_END; } | ||||
| 
 | ||||
| [a-z]+		{ return TOK_INVALID; } | ||||
| 
 | ||||
| "\\"[^ \t\r\n]+		{ rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } | ||||
| "$"[^ \t\r\n]+		{ rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } | ||||
| 
 | ||||
| [0-9]+'s?[01xzm-]*	{ rtlil_frontend_yylval.string = strdup(yytext); return TOK_VALUE; } | ||||
| -?[0-9]+		{ | ||||
| 	char *end = nullptr; | ||||
| 	errno = 0; | ||||
| 	long value = strtol(yytext, &end, 10); | ||||
| 	log_assert(end == yytext + strlen(yytext)); | ||||
| 	if (errno == ERANGE) | ||||
| 		return TOK_INVALID; // literal out of range of long | ||||
| 	if (value < INT_MIN || value > INT_MAX) | ||||
| 		return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms) | ||||
| 	rtlil_frontend_yylval.integer = value; | ||||
| 	return TOK_INT; | ||||
| } | ||||
| 
 | ||||
| \"		{ BEGIN(STRING); } | ||||
| <STRING>\\.	{ yymore(); } | ||||
| <STRING>\"	{ | ||||
| 	BEGIN(0); | ||||
| 	char *yystr = strdup(yytext); | ||||
| 	yystr[strlen(yytext) - 1] = 0; | ||||
| 	int i = 0, j = 0; | ||||
| 	while (yystr[i]) { | ||||
| 		if (yystr[i] == '\\' && yystr[i + 1]) { | ||||
| 			i++; | ||||
| 			if (yystr[i] == 'n') | ||||
| 				yystr[i] = '\n'; | ||||
| 			else if (yystr[i] == 't') | ||||
| 				yystr[i] = '\t'; | ||||
| 			else if ('0' <= yystr[i] && yystr[i] <= '7') { | ||||
| 				yystr[i] = yystr[i] - '0'; | ||||
| 				if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { | ||||
| 					yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; | ||||
| 					i++; | ||||
| 				} | ||||
| 				if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { | ||||
| 					yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; | ||||
| 					i++; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		yystr[j++] = yystr[i++]; | ||||
| 	} | ||||
| 	yystr[j] = 0; | ||||
| 	rtlil_frontend_yylval.string = yystr; | ||||
| 	return TOK_STRING; | ||||
| } | ||||
| <STRING>.	{ yymore(); } | ||||
| 
 | ||||
| "#"[^\n]*	/* ignore comments */ | ||||
| [ \t]		/* ignore non-newline whitespaces */ | ||||
| [\r\n]+		{ return TOK_EOL; } | ||||
| 
 | ||||
| .               { return *yytext; } | ||||
| 
 | ||||
| %% | ||||
| 
 | ||||
| // this is a hack to avoid the 'yyinput defined but not used' error msgs | ||||
| void *rtlil_frontend_avoid_input_warnings() { | ||||
| 	return (void*)&yyinput; | ||||
| } | ||||
|  | @ -1,525 +0,0 @@ | |||
| /* | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted, provided that the above | ||||
|  *  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. | ||||
|  * | ||||
|  *  --- | ||||
|  * | ||||
|  *  A very simple and straightforward frontend for the RTLIL text | ||||
|  *  representation. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| %require "3.0" | ||||
| 
 | ||||
| %{ | ||||
| #include <list> | ||||
| #include "frontends/rtlil/rtlil_frontend.h" | ||||
| YOSYS_NAMESPACE_BEGIN | ||||
| namespace RTLIL_FRONTEND { | ||||
| 	std::istream *lexin; | ||||
| 	RTLIL::Design *current_design; | ||||
| 	RTLIL::Module *current_module; | ||||
| 	RTLIL::Wire *current_wire; | ||||
| 	RTLIL::Memory *current_memory; | ||||
| 	RTLIL::Cell *current_cell; | ||||
| 	RTLIL::Process *current_process; | ||||
| 	std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack; | ||||
| 	std::vector<RTLIL::CaseRule*> case_stack; | ||||
| 	dict<RTLIL::IdString, RTLIL::Const> attrbuf; | ||||
| 	bool flag_nooverwrite, flag_overwrite, flag_lib; | ||||
| 	bool delete_current_module; | ||||
| } | ||||
| using namespace RTLIL_FRONTEND; | ||||
| YOSYS_NAMESPACE_END | ||||
| USING_YOSYS_NAMESPACE | ||||
| %} | ||||
| 
 | ||||
| %define api.prefix {rtlil_frontend_yy} | ||||
| 
 | ||||
| /* The union is defined in the header, so we need to provide all the | ||||
|  * includes it requires | ||||
|  */ | ||||
| %code requires { | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "frontends/rtlil/rtlil_frontend.h" | ||||
| } | ||||
| 
 | ||||
| %union { | ||||
| 	char *string; | ||||
| 	int integer; | ||||
| 	YOSYS_NAMESPACE_PREFIX RTLIL::Const *data; | ||||
| 	YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec; | ||||
| 	std::vector<YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec> *rsigspec; | ||||
| } | ||||
| 
 | ||||
| %token <string> TOK_ID TOK_VALUE TOK_STRING | ||||
| %token <integer> TOK_INT | ||||
| %token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT | ||||
| %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC | ||||
| %token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT | ||||
| %token TOK_UPDATE TOK_MEMWR TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET | ||||
| %token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO | ||||
| 
 | ||||
| %type <rsigspec> sigspec_list_reversed | ||||
| %type <sigspec> sigspec sigspec_list | ||||
| %type <integer> sync_type | ||||
| %type <data> constant | ||||
| 
 | ||||
| %expect 0 | ||||
| %debug | ||||
| 
 | ||||
| %% | ||||
| 
 | ||||
| input: | ||||
| 	optional_eol { | ||||
| 		attrbuf.clear(); | ||||
| 	} design { | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			rtlil_frontend_yyerror("dangling attribute"); | ||||
| 	}; | ||||
| 
 | ||||
| EOL: | ||||
| 	optional_eol TOK_EOL; | ||||
| 
 | ||||
| optional_eol: | ||||
| 	optional_eol TOK_EOL | /* empty */; | ||||
| 
 | ||||
| design: | ||||
| 	design module | | ||||
| 	design attr_stmt | | ||||
| 	design autoidx_stmt | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| module: | ||||
| 	TOK_MODULE TOK_ID EOL { | ||||
| 		delete_current_module = false; | ||||
| 		if (current_design->has($2)) { | ||||
| 			RTLIL::Module *existing_mod = current_design->module($2); | ||||
| 			if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) { | ||||
| 				log("Ignoring blackbox re-definition of module %s.\n", $2); | ||||
| 				delete_current_module = true; | ||||
| 			} else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { | ||||
| 				rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of module %s.", $2).c_str()); | ||||
| 			} else if (flag_nooverwrite) { | ||||
| 				log("Ignoring re-definition of module %s.\n", $2); | ||||
| 				delete_current_module = true; | ||||
| 			} else { | ||||
| 				log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", $2); | ||||
| 				current_design->remove(existing_mod); | ||||
| 			} | ||||
| 		} | ||||
| 		current_module = new RTLIL::Module; | ||||
| 		current_module->name = $2; | ||||
| 		current_module->attributes = attrbuf; | ||||
| 		if (!delete_current_module) | ||||
| 			current_design->add(current_module); | ||||
| 		attrbuf.clear(); | ||||
| 		free($2); | ||||
| 	} module_body TOK_END { | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			rtlil_frontend_yyerror("dangling attribute"); | ||||
| 		current_module->fixup_ports(); | ||||
| 		if (delete_current_module) | ||||
| 			delete current_module; | ||||
| 		else if (flag_lib) | ||||
| 			current_module->makeblackbox(); | ||||
| 		current_module = nullptr; | ||||
| 	} EOL; | ||||
| 
 | ||||
| module_body: | ||||
| 	module_body module_stmt | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| module_stmt: | ||||
| 	param_stmt | param_defval_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt; | ||||
| 
 | ||||
| param_stmt: | ||||
| 	TOK_PARAMETER TOK_ID EOL { | ||||
| 		current_module->avail_parameters($2); | ||||
| 		free($2); | ||||
| 	}; | ||||
| 
 | ||||
| param_defval_stmt: | ||||
| 	TOK_PARAMETER TOK_ID constant EOL { | ||||
| 		current_module->avail_parameters($2); | ||||
| 		current_module->parameter_default_values[$2] = *$3; | ||||
| 		delete $3; | ||||
| 		free($2); | ||||
| 	}; | ||||
| 
 | ||||
| attr_stmt: | ||||
| 	TOK_ATTRIBUTE TOK_ID constant EOL { | ||||
| 		attrbuf[$2] = *$3; | ||||
| 		delete $3; | ||||
| 		free($2); | ||||
| 	}; | ||||
| 
 | ||||
| autoidx_stmt: | ||||
| 	TOK_AUTOIDX TOK_INT EOL { | ||||
| 		autoidx = max(autoidx, $2); | ||||
| 	}; | ||||
| 
 | ||||
| wire_stmt: | ||||
| 	TOK_WIRE { | ||||
| 		current_wire = current_module->addWire("$__rtlil_frontend_tmp__"); | ||||
| 		current_wire->attributes = attrbuf; | ||||
| 		attrbuf.clear(); | ||||
| 	} wire_options TOK_ID EOL { | ||||
| 		if (current_module->wire($4) != nullptr) | ||||
| 			rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of wire %s.", $4).c_str()); | ||||
| 		current_module->rename(current_wire, $4); | ||||
| 		free($4); | ||||
| 	}; | ||||
| 
 | ||||
| wire_options: | ||||
| 	wire_options TOK_WIDTH TOK_INT { | ||||
| 		current_wire->width = $3; | ||||
| 	} | | ||||
| 	wire_options TOK_WIDTH TOK_INVALID { | ||||
| 		rtlil_frontend_yyerror("RTLIL error: invalid wire width"); | ||||
| 	} | | ||||
| 	wire_options TOK_UPTO { | ||||
| 		current_wire->upto = true; | ||||
| 	} | | ||||
| 	wire_options TOK_SIGNED { | ||||
| 		current_wire->is_signed = true; | ||||
| 	} | | ||||
| 	wire_options TOK_OFFSET TOK_INT { | ||||
| 		current_wire->start_offset = $3; | ||||
| 	} | | ||||
| 	wire_options TOK_INPUT TOK_INT { | ||||
| 		current_wire->port_id = $3; | ||||
| 		current_wire->port_input = true; | ||||
| 		current_wire->port_output = false; | ||||
| 	} | | ||||
| 	wire_options TOK_OUTPUT TOK_INT { | ||||
| 		current_wire->port_id = $3; | ||||
| 		current_wire->port_input = false; | ||||
| 		current_wire->port_output = true; | ||||
| 	} | | ||||
| 	wire_options TOK_INOUT TOK_INT { | ||||
| 		current_wire->port_id = $3; | ||||
| 		current_wire->port_input = true; | ||||
| 		current_wire->port_output = true; | ||||
| 	} | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| memory_stmt: | ||||
| 	TOK_MEMORY { | ||||
| 		current_memory = new RTLIL::Memory; | ||||
| 		current_memory->attributes = attrbuf; | ||||
| 		attrbuf.clear(); | ||||
| 	} memory_options TOK_ID EOL { | ||||
| 		if (current_module->memories.count($4) != 0) | ||||
| 			rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of memory %s.", $4).c_str()); | ||||
| 		current_memory->name = $4; | ||||
| 		current_module->memories[$4] = current_memory; | ||||
| 		free($4); | ||||
| 	}; | ||||
| 
 | ||||
| memory_options: | ||||
| 	memory_options TOK_WIDTH TOK_INT { | ||||
| 		current_memory->width = $3; | ||||
| 	} | | ||||
| 	memory_options TOK_SIZE TOK_INT { | ||||
| 		current_memory->size = $3; | ||||
| 	} | | ||||
| 	memory_options TOK_OFFSET TOK_INT { | ||||
| 		current_memory->start_offset = $3; | ||||
| 	} | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| cell_stmt: | ||||
| 	TOK_CELL TOK_ID TOK_ID EOL { | ||||
| 		if (current_module->cell($3) != nullptr) | ||||
| 			rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell %s.", $3).c_str()); | ||||
| 		current_cell = current_module->addCell($3, $2); | ||||
| 		current_cell->attributes = attrbuf; | ||||
| 		attrbuf.clear(); | ||||
| 		free($2); | ||||
| 		free($3); | ||||
| 	} cell_body TOK_END EOL; | ||||
| 
 | ||||
| cell_body: | ||||
| 	cell_body TOK_PARAMETER TOK_ID constant EOL { | ||||
| 		current_cell->parameters[$3] = *$4; | ||||
| 		free($3); | ||||
| 		delete $4; | ||||
| 	} | | ||||
| 	cell_body TOK_PARAMETER TOK_SIGNED TOK_ID constant EOL { | ||||
| 		current_cell->parameters[$4] = *$5; | ||||
| 		current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_SIGNED; | ||||
| 		free($4); | ||||
| 		delete $5; | ||||
| 	} | | ||||
| 	cell_body TOK_PARAMETER TOK_REAL TOK_ID constant EOL { | ||||
| 		current_cell->parameters[$4] = *$5; | ||||
| 		current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_REAL; | ||||
| 		free($4); | ||||
| 		delete $5; | ||||
| 	} | | ||||
| 	cell_body TOK_CONNECT TOK_ID sigspec EOL { | ||||
| 		if (current_cell->hasPort($3)) | ||||
| 			rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell port %s.", $3).c_str()); | ||||
| 		current_cell->setPort($3, *$4); | ||||
| 		delete $4; | ||||
| 		free($3); | ||||
| 	} | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| proc_stmt: | ||||
| 	TOK_PROCESS TOK_ID EOL { | ||||
| 		if (current_module->processes.count($2) != 0) | ||||
| 			rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of process %s.", $2).c_str()); | ||||
| 		current_process = current_module->addProcess($2); | ||||
| 		current_process->attributes = attrbuf; | ||||
| 		switch_stack.clear(); | ||||
| 		switch_stack.push_back(¤t_process->root_case.switches); | ||||
| 		case_stack.clear(); | ||||
| 		case_stack.push_back(¤t_process->root_case); | ||||
| 		attrbuf.clear(); | ||||
| 		free($2); | ||||
| 	} case_body sync_list TOK_END EOL; | ||||
| 
 | ||||
| switch_stmt: | ||||
| 	TOK_SWITCH sigspec EOL { | ||||
| 		RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; | ||||
| 		rule->signal = *$2; | ||||
| 		rule->attributes = attrbuf; | ||||
| 		switch_stack.back()->push_back(rule); | ||||
| 		attrbuf.clear(); | ||||
| 		delete $2; | ||||
| 	} attr_list switch_body TOK_END EOL; | ||||
| 
 | ||||
| attr_list: | ||||
| 	/* empty */ | | ||||
| 	attr_list attr_stmt; | ||||
| 
 | ||||
| switch_body: | ||||
| 	switch_body TOK_CASE { | ||||
| 		RTLIL::CaseRule *rule = new RTLIL::CaseRule; | ||||
| 		rule->attributes = attrbuf; | ||||
| 		switch_stack.back()->back()->cases.push_back(rule); | ||||
| 		switch_stack.push_back(&rule->switches); | ||||
| 		case_stack.push_back(rule); | ||||
| 		attrbuf.clear(); | ||||
| 	} compare_list EOL case_body { | ||||
| 		switch_stack.pop_back(); | ||||
| 		case_stack.pop_back(); | ||||
| 	} | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| compare_list: | ||||
| 	sigspec { | ||||
| 		case_stack.back()->compare.push_back(*$1); | ||||
| 		delete $1; | ||||
| 	} | | ||||
| 	compare_list ',' sigspec { | ||||
| 		case_stack.back()->compare.push_back(*$3); | ||||
| 		delete $3; | ||||
| 	} | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| case_body: | ||||
| 	case_body attr_stmt | | ||||
| 	case_body switch_stmt | | ||||
| 	case_body assign_stmt | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| assign_stmt: | ||||
| 	TOK_ASSIGN sigspec sigspec EOL { | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			rtlil_frontend_yyerror("dangling attribute"); | ||||
| 
 | ||||
| 		// See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this | ||||
| 		// warning | ||||
| 		if (!switch_stack.back()->empty()) { | ||||
| 			rtlil_frontend_yywarning( | ||||
| 				"case rule assign statements after switch statements may cause unexpected behaviour. " | ||||
| 				"The assign statement is reordered to come before all switch statements." | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 		case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); | ||||
| 		delete $2; | ||||
| 		delete $3; | ||||
| 	}; | ||||
| 
 | ||||
| sync_list: | ||||
| 	sync_list TOK_SYNC sync_type sigspec EOL { | ||||
| 		RTLIL::SyncRule *rule = new RTLIL::SyncRule; | ||||
| 		rule->type = RTLIL::SyncType($3); | ||||
| 		rule->signal = *$4; | ||||
| 		current_process->syncs.push_back(rule); | ||||
| 		delete $4; | ||||
| 	} update_list | | ||||
| 	sync_list TOK_SYNC TOK_ALWAYS EOL { | ||||
| 		RTLIL::SyncRule *rule = new RTLIL::SyncRule; | ||||
| 		rule->type = RTLIL::SyncType::STa; | ||||
| 		rule->signal = RTLIL::SigSpec(); | ||||
| 		current_process->syncs.push_back(rule); | ||||
| 	} update_list | | ||||
| 	sync_list TOK_SYNC TOK_GLOBAL EOL { | ||||
| 		RTLIL::SyncRule *rule = new RTLIL::SyncRule; | ||||
| 		rule->type = RTLIL::SyncType::STg; | ||||
| 		rule->signal = RTLIL::SigSpec(); | ||||
| 		current_process->syncs.push_back(rule); | ||||
| 	} update_list | | ||||
| 	sync_list TOK_SYNC TOK_INIT EOL { | ||||
| 		RTLIL::SyncRule *rule = new RTLIL::SyncRule; | ||||
| 		rule->type = RTLIL::SyncType::STi; | ||||
| 		rule->signal = RTLIL::SigSpec(); | ||||
| 		current_process->syncs.push_back(rule); | ||||
| 	} update_list | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| sync_type: | ||||
| 	TOK_LOW { $$ = RTLIL::ST0; } | | ||||
| 	TOK_HIGH { $$ = RTLIL::ST1; } | | ||||
| 	TOK_POSEDGE { $$ = RTLIL::STp; } | | ||||
| 	TOK_NEGEDGE { $$ = RTLIL::STn; } | | ||||
| 	TOK_EDGE { $$ = RTLIL::STe; }; | ||||
| 
 | ||||
| update_list: | ||||
| 	update_list TOK_UPDATE sigspec sigspec EOL { | ||||
| 		current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4)); | ||||
| 		delete $3; | ||||
| 		delete $4; | ||||
| 	} | | ||||
| 	update_list attr_list TOK_MEMWR TOK_ID sigspec sigspec sigspec constant EOL { | ||||
| 		RTLIL::MemWriteAction act; | ||||
| 		act.attributes = attrbuf; | ||||
| 		act.memid = $4; | ||||
| 		act.address = *$5; | ||||
| 		act.data = *$6; | ||||
| 		act.enable = *$7; | ||||
| 		act.priority_mask = *$8; | ||||
| 		current_process->syncs.back()->mem_write_actions.push_back(std::move(act)); | ||||
| 		attrbuf.clear(); | ||||
| 		free($4); | ||||
| 		delete $5; | ||||
| 		delete $6; | ||||
| 		delete $7; | ||||
| 		delete $8; | ||||
| 	} | | ||||
| 	/* empty */; | ||||
| 
 | ||||
| constant: | ||||
| 	TOK_VALUE { | ||||
| 		char *ep; | ||||
| 		int width = strtol($1, &ep, 10); | ||||
| 		bool is_signed = false; | ||||
| 		if (*ep == '\'') { | ||||
| 			ep++; | ||||
| 		} | ||||
| 		if (*ep == 's') { | ||||
| 			is_signed = true; | ||||
| 			ep++; | ||||
| 		} | ||||
| 		std::list<RTLIL::State> bits; | ||||
| 		while (*ep != 0) { | ||||
| 			RTLIL::State bit = RTLIL::Sx; | ||||
| 			switch (*ep) { | ||||
| 			case '0': bit = RTLIL::S0; break; | ||||
| 			case '1': bit = RTLIL::S1; break; | ||||
| 			case 'x': bit = RTLIL::Sx; break; | ||||
| 			case 'z': bit = RTLIL::Sz; break; | ||||
| 			case '-': bit = RTLIL::Sa; break; | ||||
| 			case 'm': bit = RTLIL::Sm; break; | ||||
| 			} | ||||
| 			bits.push_front(bit); | ||||
| 			ep++; | ||||
| 		} | ||||
| 
 | ||||
| 		if (bits.size() == 0) | ||||
| 			bits.push_back(RTLIL::Sx); | ||||
| 		while ((int)bits.size() < width) { | ||||
| 			RTLIL::State bit = bits.back(); | ||||
| 			if (bit == RTLIL::S1) | ||||
| 				bit = RTLIL::S0; | ||||
| 			bits.push_back(bit); | ||||
| 		} | ||||
| 		while ((int)bits.size() > width) | ||||
| 			bits.pop_back(); | ||||
| 		RTLIL::Const::Builder builder(bits.size()); | ||||
| 		for (RTLIL::State bit : bits) | ||||
| 			builder.push_back(bit); | ||||
| 		$$ = new RTLIL::Const(builder.build()); | ||||
| 		if (is_signed) { | ||||
| 			$$->flags |= RTLIL::CONST_FLAG_SIGNED; | ||||
| 		} | ||||
| 		free($1); | ||||
| 	} | | ||||
| 	TOK_INT { | ||||
| 		$$ = new RTLIL::Const($1); | ||||
| 	} | | ||||
| 	TOK_STRING { | ||||
| 		$$ = new RTLIL::Const($1); | ||||
| 		free($1); | ||||
| 	}; | ||||
| 
 | ||||
| sigspec: | ||||
| 	constant { | ||||
| 		$$ = new RTLIL::SigSpec(*$1); | ||||
| 		delete $1; | ||||
| 	} | | ||||
| 	TOK_ID { | ||||
| 		if (current_module->wire($1) == nullptr) | ||||
| 			rtlil_frontend_yyerror(stringf("RTLIL error: wire %s not found", $1).c_str()); | ||||
| 		$$ = new RTLIL::SigSpec(current_module->wire($1)); | ||||
| 		free($1); | ||||
| 	} | | ||||
| 	sigspec '[' TOK_INT ']' { | ||||
| 		if ($3 >= $1->size() || $3 < 0) | ||||
| 			rtlil_frontend_yyerror("bit index out of range"); | ||||
| 		$$ = new RTLIL::SigSpec($1->extract($3)); | ||||
| 		delete $1; | ||||
| 	} | | ||||
| 	sigspec '[' TOK_INT ':' TOK_INT ']' { | ||||
| 		if ($3 >= $1->size() || $3 < 0 || $3 < $5) | ||||
| 			rtlil_frontend_yyerror("invalid slice"); | ||||
| 		$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1)); | ||||
| 		delete $1; | ||||
| 	} | | ||||
| 	'{' sigspec_list '}' { | ||||
| 		$$ = $2; | ||||
| 	}; | ||||
| 
 | ||||
| sigspec_list_reversed: | ||||
| 	sigspec_list_reversed sigspec { | ||||
| 		$$->push_back(*$2); | ||||
| 		delete $2; | ||||
| 	} | | ||||
| 	/* empty */ { | ||||
| 		$$ = new std::vector<RTLIL::SigSpec>; | ||||
| 	}; | ||||
| 
 | ||||
| sigspec_list: sigspec_list_reversed { | ||||
| 		$$ = new RTLIL::SigSpec; | ||||
| 		for (auto it = $1->rbegin(); it != $1->rend(); it++) | ||||
| 			$$->append(*it); | ||||
| 		delete $1; | ||||
| 	}; | ||||
| 
 | ||||
| conn_stmt: | ||||
| 	TOK_CONNECT sigspec sigspec EOL { | ||||
| 		if (attrbuf.size() != 0) | ||||
| 			rtlil_frontend_yyerror("dangling attribute"); | ||||
| 		current_module->connect(*$2, *$3); | ||||
| 		delete $2; | ||||
| 		delete $3; | ||||
| 	}; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue