mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	ast: use new format string helpers.
This commit is contained in:
		
							parent
							
								
									9ea241711e
								
							
						
					
					
						commit
						9f8e039a4b
					
				
					 4 changed files with 48 additions and 161 deletions
				
			
		|  | @ -30,6 +30,7 @@ | ||||||
| #define AST_H | #define AST_H | ||||||
| 
 | 
 | ||||||
| #include "kernel/rtlil.h" | #include "kernel/rtlil.h" | ||||||
|  | #include "kernel/fmt.h" | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <set> | #include <set> | ||||||
| 
 | 
 | ||||||
|  | @ -277,7 +278,7 @@ namespace AST | ||||||
| 		bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); | 		bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); | ||||||
| 		AstNode *eval_const_function(AstNode *fcall, bool must_succeed); | 		AstNode *eval_const_function(AstNode *fcall, bool must_succeed); | ||||||
| 		bool is_simple_const_expr(); | 		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); | 		Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0); | ||||||
| 
 | 
 | ||||||
| 		bool is_recursive_function() const; | 		bool is_recursive_function() const; | ||||||
| 		std::pair<AstNode*, AstNode*> get_tern_choice(); | 		std::pair<AstNode*, AstNode*> get_tern_choice(); | ||||||
|  |  | ||||||
|  | @ -43,142 +43,36 @@ using namespace AST_INTERNAL; | ||||||
| 
 | 
 | ||||||
| // Process a format string and arguments for $display, $write, $sprintf, etc
 | // Process a format string and arguments for $display, $write, $sprintf, etc
 | ||||||
| 
 | 
 | ||||||
| std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) { | Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at) { | ||||||
| 	// Other arguments are placeholders. Process the string as we go through it
 | 	std::vector<VerilogFmtArg> args; | ||||||
| 	std::string sout; | 	for (size_t index = first_arg_at; index < children.size(); index++) { | ||||||
| 	for (size_t i = 0; i < sformat.length(); i++) | 		AstNode *node_arg = children[index]; | ||||||
| 	{ | 		while (node_arg->simplify(true, false, stage, -1, false, false)) { } | ||||||
| 		// format specifier
 |  | ||||||
| 		if (sformat[i] == '%') |  | ||||||
| 		{ |  | ||||||
| 			// If there's no next character, that's a problem
 |  | ||||||
| 			if (i+1 >= sformat.length()) |  | ||||||
| 				input_error("System task `%s' called with `%%' at end of string.\n", str.c_str()); |  | ||||||
| 
 |  | ||||||
| 			char cformat = sformat[++i]; |  | ||||||
| 
 |  | ||||||
| 			// %% is special, does not need a matching argument
 |  | ||||||
| 			if (cformat == '%') |  | ||||||
| 			{ |  | ||||||
| 				sout += '%'; |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			bool got_len = false; |  | ||||||
| 			bool got_zlen = false; |  | ||||||
| 			int len_value = 0; |  | ||||||
| 
 |  | ||||||
| 			while ('0' <= cformat && cformat <= '9') |  | ||||||
| 			{ |  | ||||||
| 				if (!got_len && cformat == '0') |  | ||||||
| 					got_zlen = true; |  | ||||||
| 
 |  | ||||||
| 				got_len = true; |  | ||||||
| 				len_value = 10*len_value + (cformat - '0'); |  | ||||||
| 
 |  | ||||||
| 				cformat = sformat[++i]; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Simplify the argument
 |  | ||||||
| 			AstNode *node_arg = nullptr; |  | ||||||
| 
 |  | ||||||
| 			// Everything from here on depends on the format specifier
 |  | ||||||
| 			switch (cformat) |  | ||||||
| 			{ |  | ||||||
| 				case 's': |  | ||||||
| 				case 'S': |  | ||||||
| 				case 'd': |  | ||||||
| 				case 'D': |  | ||||||
| 					if (got_len && len_value != 0) |  | ||||||
| 						goto unsupported_format; |  | ||||||
| 					YS_FALLTHROUGH |  | ||||||
| 				case 'x': |  | ||||||
| 				case 'X': |  | ||||||
| 					if (next_arg >= GetSize(children)) |  | ||||||
| 						input_error("Missing argument for %%%c format specifier in system task `%s'.\n", |  | ||||||
| 								cformat, str.c_str()); |  | ||||||
| 
 |  | ||||||
| 					node_arg = children[next_arg++]; |  | ||||||
| 					while (node_arg->simplify(true, false, stage, width_hint, sign_hint, false)) { } |  | ||||||
| 		if (node_arg->type != AST_CONSTANT) | 		if (node_arg->type != AST_CONSTANT) | ||||||
| 						input_error("Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); | 			input_error("Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); | ||||||
| 					break; |  | ||||||
| 
 | 
 | ||||||
| 				case 'm': | 		VerilogFmtArg arg = {}; | ||||||
| 				case 'M': | 		arg.filename = filename; | ||||||
| 					if (got_len) | 		arg.first_line = location.first_line; | ||||||
| 						goto unsupported_format; | 		if (node_arg->is_string) { | ||||||
| 					break; | 			arg.type = VerilogFmtArg::STRING; | ||||||
| 
 | 			arg.str = node_arg->bitsAsConst().decode_string(); | ||||||
| 				case 'l': | 			// and in case this will be used as an argument...
 | ||||||
| 				case 'L': | 			arg.sig = node_arg->bitsAsConst(); | ||||||
| 					if (got_len) | 			arg.signed_ = false; | ||||||
| 						goto unsupported_format; | 		} else { | ||||||
| 					break; | 			arg.type = VerilogFmtArg::INTEGER; | ||||||
| 
 | 			arg.sig = node_arg->bitsAsConst(); | ||||||
| 				default: | 			arg.signed_ = node_arg->is_signed; | ||||||
| 				unsupported_format: | 		} | ||||||
| 					input_error("System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); | 		args.push_back(arg); | ||||||
| 					break; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 			switch (cformat) | 	Fmt fmt = {}; | ||||||
| 			{ | 	fmt.parse_verilog(args, sformat_like, default_base, /*task_name=*/str, current_module->name); | ||||||
| 				case 's': | 	return fmt; | ||||||
| 				case 'S': |  | ||||||
| 					sout += node_arg->bitsAsConst().decode_string(); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case 'd': |  | ||||||
| 				case 'D': |  | ||||||
| 					sout += stringf("%d", node_arg->bitsAsConst().as_int()); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case 'x': |  | ||||||
| 				case 'X': |  | ||||||
| 					{ |  | ||||||
| 						Const val = node_arg->bitsAsConst(); |  | ||||||
| 
 |  | ||||||
| 						while (GetSize(val) % 4 != 0) |  | ||||||
| 							val.bits.push_back(State::S0); |  | ||||||
| 
 |  | ||||||
| 						int len = GetSize(val) / 4; |  | ||||||
| 						for (int i = len; i < len_value; i++) |  | ||||||
| 							sout += got_zlen ? '0' : ' '; |  | ||||||
| 
 |  | ||||||
| 						for (int i = len-1; i >= 0; i--) { |  | ||||||
| 							Const digit = val.extract(4*i, 4); |  | ||||||
| 							if (digit.is_fully_def()) |  | ||||||
| 								sout += stringf(cformat == 'x' ? "%x" : "%X", digit.as_int()); |  | ||||||
| 							else |  | ||||||
| 								sout += cformat == 'x' ? "x" : "X"; |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case 'm': |  | ||||||
| 				case 'M': |  | ||||||
| 					sout += log_id(current_module->name); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case 'l': |  | ||||||
| 				case 'L': |  | ||||||
| 					sout += log_id(current_module->name); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				default: |  | ||||||
| 					log_abort(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// not a format specifier
 |  | ||||||
| 		else |  | ||||||
| 			sout += sformat[i]; |  | ||||||
| 	} |  | ||||||
| 	return sout; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| void AstNode::annotateTypedEnums(AstNode *template_node) | void AstNode::annotateTypedEnums(AstNode *template_node) | ||||||
| { | { | ||||||
| 	//check if enum
 | 	//check if enum
 | ||||||
|  | @ -1057,33 +951,30 @@ bool AstNode::simplify(bool const_fold, bool in_lvalue, int stage, int width_hin | ||||||
| 		str = std::string(); | 		str = std::string(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) { | 	if ((type == AST_TCALL) && (str.substr(0, 8) == "$display" || str.substr(0, 6) == "$write") && (!current_always || current_always->type != AST_INITIAL)) { | ||||||
| 		log_file_warning(filename, location.first_line, "System task `%s' outside initial block is unsupported.\n", str.c_str()); | 		log_file_warning(filename, location.first_line, "System task `%s' outside initial block is unsupported.\n", str.c_str()); | ||||||
| 		delete_children(); | 		delete_children(); | ||||||
| 		str = std::string(); | 		str = std::string(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// print messages if this a call to $display() or $write()
 | 	// print messages if this a call to $display() or $write() family of functions
 | ||||||
| 	// This code implements only a small subset of Verilog-2005 $display() format specifiers,
 | 	if ((type == AST_TCALL) && | ||||||
| 	// but should be good enough for most uses
 | 	    (str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" || | ||||||
| 	if ((type == AST_TCALL) && ((str == "$display") || (str == "$write"))) | 	     str == "$write"   || str == "$writeb"   || str == "$writeh"   || str == "$writeo")) | ||||||
| 	{ | 	{ | ||||||
| 		int nargs = GetSize(children); | 		int default_base = 10; | ||||||
| 		if (nargs < 1) | 		if (str.back() == 'b') | ||||||
| 			input_error("System task `%s' got %d arguments, expected >= 1.\n", | 			default_base = 2; | ||||||
| 					str.c_str(), int(children.size())); | 		else if (str.back() == 'o') | ||||||
|  | 			default_base = 8; | ||||||
|  | 		else if (str.back() == 'h') | ||||||
|  | 			default_base = 16; | ||||||
|  | 
 | ||||||
|  | 		Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base); | ||||||
|  | 		if (str.substr(0, 8) == "$display") | ||||||
|  | 			fmt.append_string("\n"); | ||||||
|  | 		log("%s", fmt.render().c_str()); | ||||||
| 
 | 
 | ||||||
| 		// First argument is the format string
 |  | ||||||
| 		AstNode *node_string = children[0]; |  | ||||||
| 		while (node_string->simplify(true, false, stage, width_hint, sign_hint, false)) { } |  | ||||||
| 		if (node_string->type != AST_CONSTANT) |  | ||||||
| 			input_error("Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str()); |  | ||||||
| 		std::string sformat = node_string->bitsAsConst().decode_string(); |  | ||||||
| 		std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); |  | ||||||
| 		// Finally, print the message (only include a \n for $display, not for $write)
 |  | ||||||
| 		log("%s", sout.c_str()); |  | ||||||
| 		if (str == "$display") |  | ||||||
| 			log("\n"); |  | ||||||
| 		delete_children(); | 		delete_children(); | ||||||
| 		str = std::string(); | 		str = std::string(); | ||||||
| 	} | 	} | ||||||
|  | @ -3735,13 +3626,8 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (str == "\\$sformatf") { | 			if (str == "\\$sformatf") { | ||||||
| 				AstNode *node_string = children[0]; | 				Fmt fmt = processFormat(stage, /*sformat_like=*/true); | ||||||
| 				while (node_string->simplify(true, false, stage, width_hint, sign_hint, false)) { } | 				newNode = AstNode::mkconst_str(fmt.render()); | ||||||
| 				if (node_string->type != AST_CONSTANT) |  | ||||||
| 					input_error("Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); |  | ||||||
| 				std::string sformat = node_string->bitsAsConst().decode_string(); |  | ||||||
| 				std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); |  | ||||||
| 				newNode = AstNode::mkconst_str(sout); |  | ||||||
| 				goto apply_newNode; | 				goto apply_newNode; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -386,7 +386,7 @@ and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { | ||||||
| supply0 { return TOK_SUPPLY0; } | supply0 { return TOK_SUPPLY0; } | ||||||
| supply1 { return TOK_SUPPLY1; } | supply1 { return TOK_SUPPLY1; } | ||||||
| 
 | 
 | ||||||
| "$"(display|write|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { | "$"(display[bho]?|write[bho]?|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { | ||||||
| 	yylval->string = new std::string(yytext); | 	yylval->string = new std::string(yytext); | ||||||
| 	return TOK_ID; | 	return TOK_ID; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ module top; | ||||||
| 	localparam b = $sformatf("%d", 4'b011); | 	localparam b = $sformatf("%d", 4'b011); | ||||||
| 	generate | 	generate | ||||||
| 		if (a != "0x5a") $error("a incorrect!"); | 		if (a != "0x5a") $error("a incorrect!"); | ||||||
| 		if (b != "3") $error("b incorrect!"); | 		if (b != " 3") $error("b incorrect!"); | ||||||
| 	endgenerate | 	endgenerate | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue