mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-29 18:52:30 +00:00 
			
		
		
		
	ast: Add support for $sformatf system function
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
		
							parent
							
								
									b7be6cfd65
								
							
						
					
					
						commit
						22c967e35e
					
				
					 3 changed files with 122 additions and 93 deletions
				
			
		|  | @ -244,6 +244,7 @@ namespace AST | ||||||
| 		void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); | 		void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); | ||||||
| 		AstNode *eval_const_function(AstNode *fcall); | 		AstNode *eval_const_function(AstNode *fcall); | ||||||
| 		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); | ||||||
| 
 | 
 | ||||||
| 		// create a human-readable text representation of the AST (for debugging)
 | 		// create a human-readable text representation of the AST (for debugging)
 | ||||||
| 		void dumpAst(FILE *f, std::string indent) const; | 		void dumpAst(FILE *f, std::string indent) const; | ||||||
|  |  | ||||||
|  | @ -41,6 +41,103 @@ YOSYS_NAMESPACE_BEGIN | ||||||
| using namespace AST; | using namespace AST; | ||||||
| using namespace AST_INTERNAL; | using namespace AST_INTERNAL; | ||||||
| 
 | 
 | ||||||
|  | // 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) { | ||||||
|  | 	// Other arguments are placeholders. Process the string as we go through it
 | ||||||
|  | 	std::string sout; | ||||||
|  | 	for (size_t i = 0; i < sformat.length(); i++) | ||||||
|  | 	{ | ||||||
|  | 		// format specifier
 | ||||||
|  | 		if (sformat[i] == '%') | ||||||
|  | 		{ | ||||||
|  | 			// If there's no next character, that's a problem
 | ||||||
|  | 			if (i+1 >= sformat.length()) | ||||||
|  | 				log_file_error(filename, linenum, "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; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// 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': | ||||||
|  | 				case 'x': | ||||||
|  | 				case 'X': | ||||||
|  | 					if (next_arg >= GetSize(children)) | ||||||
|  | 						log_file_error(filename, linenum, "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, false, stage, width_hint, sign_hint, false)) { } | ||||||
|  | 					if (node_arg->type != AST_CONSTANT) | ||||||
|  | 						log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				case 'm': | ||||||
|  | 				case 'M': | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				default: | ||||||
|  | 					log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			switch (cformat) | ||||||
|  | 			{ | ||||||
|  | 				case 's': | ||||||
|  | 				case 'S': | ||||||
|  | 					sout += node_arg->bitsAsConst().decode_string(); | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				case 'd': | ||||||
|  | 				case 'D': | ||||||
|  | 					{ | ||||||
|  | 						char tmp[128]; | ||||||
|  | 						snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); | ||||||
|  | 						sout += tmp; | ||||||
|  | 					} | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				case 'x': | ||||||
|  | 				case 'X': | ||||||
|  | 					{ | ||||||
|  | 						char tmp[128]; | ||||||
|  | 						snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); | ||||||
|  | 						sout += tmp; | ||||||
|  | 					} | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				case 'm': | ||||||
|  | 				case 'M': | ||||||
|  | 					sout += log_id(current_module->name); | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				default: | ||||||
|  | 					log_abort(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// not a format specifier
 | ||||||
|  | 		else | ||||||
|  | 			sout += sformat[i]; | ||||||
|  | 	} | ||||||
|  | 	return sout; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| // convert the AST into a simpler AST that has all parameters substituted by their
 | // 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
 | // values, unrolled for-loops, expanded generate blocks, etc. when this function
 | ||||||
| // is done with an AST it can be converted into RTLIL using genRTLIL().
 | // is done with an AST it can be converted into RTLIL using genRTLIL().
 | ||||||
|  | @ -216,99 +313,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 		if (node_string->type != AST_CONSTANT) | 		if (node_string->type != AST_CONSTANT) | ||||||
| 			log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str()); | 			log_file_error(filename, linenum, "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 sformat = node_string->bitsAsConst().decode_string(); | ||||||
| 
 | 		std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); | ||||||
| 		// Other arguments are placeholders. Process the string as we go through it
 |  | ||||||
| 		std::string sout; |  | ||||||
| 		int next_arg = 1; |  | ||||||
| 		for (size_t i = 0; i < sformat.length(); i++) |  | ||||||
| 		{ |  | ||||||
| 			// format specifier
 |  | ||||||
| 			if (sformat[i] == '%') |  | ||||||
| 			{ |  | ||||||
| 				// If there's no next character, that's a problem
 |  | ||||||
| 				if (i+1 >= sformat.length()) |  | ||||||
| 					log_file_error(filename, linenum, "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; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				// 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': |  | ||||||
| 					case 'x': |  | ||||||
| 					case 'X': |  | ||||||
| 						if (next_arg >= GetSize(children)) |  | ||||||
| 							log_file_error(filename, linenum, "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, false, stage, width_hint, sign_hint, false)) { } |  | ||||||
| 						if (node_arg->type != AST_CONSTANT) |  | ||||||
| 							log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); |  | ||||||
| 						break; |  | ||||||
| 
 |  | ||||||
| 					case 'm': |  | ||||||
| 					case 'M': |  | ||||||
| 						break; |  | ||||||
| 
 |  | ||||||
| 					default: |  | ||||||
| 						log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); |  | ||||||
| 						break; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				switch (cformat) |  | ||||||
| 				{ |  | ||||||
| 					case 's': |  | ||||||
| 					case 'S': |  | ||||||
| 						sout += node_arg->bitsAsConst().decode_string(); |  | ||||||
| 						break; |  | ||||||
| 
 |  | ||||||
| 					case 'd': |  | ||||||
| 					case 'D': |  | ||||||
| 						{ |  | ||||||
| 							char tmp[128]; |  | ||||||
| 							snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); |  | ||||||
| 							sout += tmp; |  | ||||||
| 						} |  | ||||||
| 						break; |  | ||||||
| 
 |  | ||||||
| 					case 'x': |  | ||||||
| 					case 'X': |  | ||||||
| 						{ |  | ||||||
| 							char tmp[128]; |  | ||||||
| 							snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); |  | ||||||
| 							sout += tmp; |  | ||||||
| 						} |  | ||||||
| 						break; |  | ||||||
| 
 |  | ||||||
| 					case 'm': |  | ||||||
| 					case 'M': |  | ||||||
| 						sout += log_id(current_module->name); |  | ||||||
| 						break; |  | ||||||
| 
 |  | ||||||
| 					default: |  | ||||||
| 						log_abort(); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// not a format specifier
 |  | ||||||
| 			else |  | ||||||
| 				sout += sformat[i]; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Finally, print the message (only include a \n for $display, not for $write)
 | 		// Finally, print the message (only include a \n for $display, not for $write)
 | ||||||
| 		log("%s", sout.c_str()); | 		log("%s", sout.c_str()); | ||||||
| 		if (str == "$display") | 		if (str == "$display") | ||||||
|  | @ -2244,6 +2249,17 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 				goto apply_newNode; | 				goto apply_newNode; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			if (str == "\\$sformatf") { | ||||||
|  | 				AstNode *node_string = children[0]; | ||||||
|  | 				while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } | ||||||
|  | 				if (node_string->type != AST_CONSTANT) | ||||||
|  | 					log_file_error(filename, linenum, "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; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) | 			if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) | ||||||
| 			{ | 			{ | ||||||
| 				AstNode *dpi_decl = current_scope[str]; | 				AstNode *dpi_decl = current_scope[str]; | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								tests/various/sformatf.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/various/sformatf.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | read_verilog <<EOT | ||||||
|  | 
 | ||||||
|  | module top; | ||||||
|  | 	localparam a = $sformatf("0x%x", 8'h5A); | ||||||
|  | 	localparam b = $sformatf("%d", 4'b011); | ||||||
|  | 	generate | ||||||
|  | 		if (a != "0x5a") $error("a incorrect!"); | ||||||
|  | 		if (b != "3") $error("b incorrect!"); | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | EOT | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue