mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	kernel: add format string helpers, fmt.
				
					
				
			This commit is contained in:
		
							parent
							
								
									f8e2c955fc
								
							
						
					
					
						commit
						9ea241711e
					
				
					 3 changed files with 673 additions and 6 deletions
				
			
		
							
								
								
									
										577
									
								
								kernel/fmt.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										577
									
								
								kernel/fmt.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,577 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  yosys -- Yosys Open SYnthesis Suite
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2020  whitequark <whitequark@whitequark.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 *  purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 *  copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "libs/bigint/BigUnsigned.hh"
 | 
			
		||||
#include "kernel/fmt.h"
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
 | 
			
		||||
void Fmt::append_string(const std::string &str) {
 | 
			
		||||
	FmtPart part = {};
 | 
			
		||||
	part.type = FmtPart::STRING;
 | 
			
		||||
	part.str = str;
 | 
			
		||||
	parts.push_back(part);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Fmt::parse_rtlil(RTLIL::Cell *cell) {
 | 
			
		||||
	std::string fmt = cell->getParam(ID(FORMAT)).decode_string();
 | 
			
		||||
	RTLIL::SigSpec args = cell->getPort(ID(ARGS));
 | 
			
		||||
	parts.clear();
 | 
			
		||||
 | 
			
		||||
	FmtPart part;
 | 
			
		||||
	for (size_t i = 0; i < fmt.size(); i++) {
 | 
			
		||||
		if (fmt.substr(i, 2) == "}}")
 | 
			
		||||
			part.str += '}';
 | 
			
		||||
		else if (fmt.substr(i, 2) == "{{")
 | 
			
		||||
			part.str += '{';
 | 
			
		||||
		else if (fmt[i] == '}')
 | 
			
		||||
			log_assert(false && "Unexpected '}' in format string");
 | 
			
		||||
		else if (fmt[i] == '{') {
 | 
			
		||||
			if (!part.str.empty()) {
 | 
			
		||||
				part.type = FmtPart::STRING;
 | 
			
		||||
				parts.push_back(part);
 | 
			
		||||
				part = {};
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			size_t arg_size = 0;
 | 
			
		||||
			for (++i; i < fmt.size(); i++) {
 | 
			
		||||
				if (fmt[i] >= '0' && fmt[i] <= '9') {
 | 
			
		||||
					arg_size *= 10;
 | 
			
		||||
					arg_size += fmt[i] - '0';
 | 
			
		||||
				} else if (fmt[i] == ':') {
 | 
			
		||||
					break;
 | 
			
		||||
				} else {
 | 
			
		||||
					log_assert(false && "Unexpected character in format substitution");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (++i == fmt.size())
 | 
			
		||||
				log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
 | 
			
		||||
			if ((size_t)args.size() < arg_size)
 | 
			
		||||
				log_assert(false && "Format part overruns arguments");
 | 
			
		||||
			part.sig = args.extract(0, arg_size);
 | 
			
		||||
			args.remove(0, arg_size);
 | 
			
		||||
 | 
			
		||||
			if (fmt[i] == '>')
 | 
			
		||||
				part.justify = FmtPart::RIGHT;
 | 
			
		||||
			else if (fmt[i] == '<')
 | 
			
		||||
				part.justify = FmtPart::LEFT;
 | 
			
		||||
			else
 | 
			
		||||
				log_assert(false && "Unexpected justification in format substitution");
 | 
			
		||||
			if (++i == fmt.size())
 | 
			
		||||
				log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
 | 
			
		||||
			if (fmt[i] == '0' || fmt[i] == ' ')
 | 
			
		||||
				part.padding = fmt[i];
 | 
			
		||||
			else
 | 
			
		||||
				log_assert(false && "Unexpected padding in format substitution");
 | 
			
		||||
			if (++i == fmt.size())
 | 
			
		||||
				log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
 | 
			
		||||
			for (; i < fmt.size(); i++) {
 | 
			
		||||
				if (fmt[i] >= '0' && fmt[i] <= '9') {
 | 
			
		||||
					part.width *= 10;
 | 
			
		||||
					part.width += fmt[i] - '0';
 | 
			
		||||
					continue;
 | 
			
		||||
				} else if (fmt[i] == 'b') {
 | 
			
		||||
					part.type = FmtPart::INTEGER;
 | 
			
		||||
					part.base = 2;
 | 
			
		||||
				} else if (fmt[i] == 'o') {
 | 
			
		||||
					part.type = FmtPart::INTEGER;
 | 
			
		||||
					part.base = 8;
 | 
			
		||||
				} else if (fmt[i] == 'd') {
 | 
			
		||||
					part.type = FmtPart::INTEGER;
 | 
			
		||||
					part.base = 10;
 | 
			
		||||
				} else if (fmt[i] == 'h') {
 | 
			
		||||
					part.type = FmtPart::INTEGER;
 | 
			
		||||
					part.base = 16;
 | 
			
		||||
				} else if (fmt[i] == 'c') {
 | 
			
		||||
					part.type = FmtPart::CHARACTER;
 | 
			
		||||
				} else {
 | 
			
		||||
					log_assert(false && "Unexpected character in format substitution");
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (++i == fmt.size())
 | 
			
		||||
				log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
 | 
			
		||||
			if (part.type == FmtPart::INTEGER) {
 | 
			
		||||
				if (fmt[i] == '+') {
 | 
			
		||||
					part.plus = true;
 | 
			
		||||
					if (++i == fmt.size())
 | 
			
		||||
						log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (fmt[i] == '0') {
 | 
			
		||||
					part.lzero = true;
 | 
			
		||||
					if (++i == fmt.size())
 | 
			
		||||
						log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (fmt[i] == 'u')
 | 
			
		||||
					part.signed_ = false;
 | 
			
		||||
				else if (fmt[i] == 's')
 | 
			
		||||
					part.signed_ = true;
 | 
			
		||||
				else
 | 
			
		||||
					log_assert(false && "Unexpected character in format substitution");
 | 
			
		||||
				if (++i == fmt.size())
 | 
			
		||||
					log_assert(false && "Unexpected end in format substitution");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (fmt[i] != '}')
 | 
			
		||||
				log_assert(false && "Expected '}' after format substitution");
 | 
			
		||||
 | 
			
		||||
			parts.push_back(part);
 | 
			
		||||
			part = {};
 | 
			
		||||
		} else {
 | 
			
		||||
			part.str += fmt[i];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!part.str.empty()) {
 | 
			
		||||
		part.type = FmtPart::STRING;
 | 
			
		||||
		parts.push_back(part);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
 | 
			
		||||
	std::string fmt;
 | 
			
		||||
	RTLIL::SigSpec args;
 | 
			
		||||
 | 
			
		||||
	for (auto &part : parts) {
 | 
			
		||||
		switch (part.type) {
 | 
			
		||||
			case FmtPart::STRING:
 | 
			
		||||
				for (char c : part.str) {
 | 
			
		||||
					if (c == '{')
 | 
			
		||||
						fmt += "{{";
 | 
			
		||||
					else if (c == '}')
 | 
			
		||||
						fmt += "}}";
 | 
			
		||||
					else
 | 
			
		||||
						fmt += c;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case FmtPart::CHARACTER:
 | 
			
		||||
				log_assert(part.sig.size() % 8 == 0);
 | 
			
		||||
				YS_FALLTHROUGH
 | 
			
		||||
			case FmtPart::INTEGER:
 | 
			
		||||
				args.append(part.sig);
 | 
			
		||||
				fmt += '{';
 | 
			
		||||
				fmt += std::to_string(part.sig.size());
 | 
			
		||||
				fmt += ':';
 | 
			
		||||
				if (part.justify == FmtPart::RIGHT)
 | 
			
		||||
					fmt += '>';
 | 
			
		||||
				else if (part.justify == FmtPart::LEFT)
 | 
			
		||||
					fmt += '<';
 | 
			
		||||
				else log_abort();
 | 
			
		||||
				log_assert(part.padding == '0' || part.padding == ' ');
 | 
			
		||||
				fmt += part.padding;
 | 
			
		||||
				fmt += std::to_string(part.width);
 | 
			
		||||
				if (part.type == FmtPart::INTEGER) {
 | 
			
		||||
					switch (part.base) {
 | 
			
		||||
						case  2: fmt += 'b'; break;
 | 
			
		||||
						case  8: fmt += 'o'; break;
 | 
			
		||||
						case 10: fmt += 'd'; break;
 | 
			
		||||
						case 16: fmt += 'h'; break;
 | 
			
		||||
						default: log_abort();
 | 
			
		||||
					}
 | 
			
		||||
					if (part.plus)
 | 
			
		||||
						fmt += '+';
 | 
			
		||||
					if (part.lzero)
 | 
			
		||||
						fmt += '0';
 | 
			
		||||
					fmt += part.signed_ ? 's' : 'u';
 | 
			
		||||
				} else if (part.type == FmtPart::CHARACTER) {
 | 
			
		||||
					fmt += 'c';
 | 
			
		||||
				} else log_abort();
 | 
			
		||||
				fmt += '}';
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			default: log_abort();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cell->setParam(ID(FORMAT), fmt);
 | 
			
		||||
	cell->setParam(ID(ARGS_WIDTH), args.size());
 | 
			
		||||
	cell->setPort(ID(ARGS), args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t compute_requried_decimal_places(size_t size, bool signed_)
 | 
			
		||||
{
 | 
			
		||||
	BigUnsigned max;
 | 
			
		||||
	if (!signed_)
 | 
			
		||||
		max.setBit(size, true);
 | 
			
		||||
	else
 | 
			
		||||
		max.setBit(size - 1, true);
 | 
			
		||||
	size_t places = 0;
 | 
			
		||||
	while (!max.isZero()) {
 | 
			
		||||
		places++;
 | 
			
		||||
		max /= 10;
 | 
			
		||||
	}
 | 
			
		||||
	if (signed_)
 | 
			
		||||
		places++;
 | 
			
		||||
	return places;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void apply_verilog_automatic_sizing(FmtPart &part)
 | 
			
		||||
{
 | 
			
		||||
	if (part.base == 10) {
 | 
			
		||||
		size_t places = compute_requried_decimal_places(part.sig.size(), part.signed_);
 | 
			
		||||
		part.padding = ' ';
 | 
			
		||||
		part.width = std::max(part.width, places);
 | 
			
		||||
	} else {
 | 
			
		||||
		part.lzero = true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name)
 | 
			
		||||
{
 | 
			
		||||
	parts.clear();
 | 
			
		||||
 | 
			
		||||
	auto arg = args.begin();
 | 
			
		||||
	for (; arg != args.end(); ++arg) {
 | 
			
		||||
		switch (arg->type) {
 | 
			
		||||
			case VerilogFmtArg::INTEGER: {
 | 
			
		||||
				FmtPart part = {};
 | 
			
		||||
				part.type = FmtPart::INTEGER;
 | 
			
		||||
				part.sig = arg->sig;
 | 
			
		||||
				part.base = default_base;
 | 
			
		||||
				part.signed_ = arg->signed_;
 | 
			
		||||
				apply_verilog_automatic_sizing(part);
 | 
			
		||||
				parts.push_back(part);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case VerilogFmtArg::STRING: {
 | 
			
		||||
				if (arg == args.begin() || !sformat_like) {
 | 
			
		||||
					const auto fmtarg = arg;
 | 
			
		||||
					const std::string &fmt = fmtarg->str;
 | 
			
		||||
					FmtPart part = {};
 | 
			
		||||
					for (size_t i = 0; i < fmt.size(); i++) {
 | 
			
		||||
						if (fmt[i] != '%') {
 | 
			
		||||
							part.str += fmt[i];
 | 
			
		||||
						} else if (fmt.substr(i, 2) == "%%") {
 | 
			
		||||
							i++;
 | 
			
		||||
							part.str += '%';
 | 
			
		||||
						} else if (fmt.substr(i, 2) == "%l" || fmt.substr(i, 2) == "%L") {
 | 
			
		||||
							i++;
 | 
			
		||||
							part.str += module_name.str();
 | 
			
		||||
						} else {
 | 
			
		||||
							if (!part.str.empty()) {
 | 
			
		||||
								part.type = FmtPart::STRING;
 | 
			
		||||
								parts.push_back(part);
 | 
			
		||||
								part = {};
 | 
			
		||||
							}
 | 
			
		||||
							if (++i == fmt.size()) {
 | 
			
		||||
								log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if (++arg == args.end()) {
 | 
			
		||||
								log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with fewer arguments than the format specifiers in argument %zu require.\n", task_name.c_str(), fmtarg - args.begin() + 1);
 | 
			
		||||
							}
 | 
			
		||||
							part.sig = arg->sig;
 | 
			
		||||
							part.signed_ = arg->signed_;
 | 
			
		||||
 | 
			
		||||
							for (; i < fmt.size(); i++) {
 | 
			
		||||
								if (fmt[i] == '-') {
 | 
			
		||||
									// left justify; not in IEEE 1800-2017 or verilator but iverilog has it
 | 
			
		||||
									part.justify = FmtPart::LEFT;
 | 
			
		||||
								} else if (fmt[i] == '+') {
 | 
			
		||||
									// always show sign; not in IEEE 1800-2017 or verilator but iverilog has it
 | 
			
		||||
									part.plus = true;
 | 
			
		||||
								} else break;
 | 
			
		||||
							}
 | 
			
		||||
							if (i == fmt.size()) {
 | 
			
		||||
								log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							bool has_leading_zero = false, has_width = false;
 | 
			
		||||
							for (; i < fmt.size(); i++) {
 | 
			
		||||
								if (fmt[i] >= '0' && fmt[i] <= '9') {
 | 
			
		||||
									if (fmt[i] == '0' && !has_width) {
 | 
			
		||||
										has_leading_zero = true;
 | 
			
		||||
									} else {
 | 
			
		||||
										has_width = true;
 | 
			
		||||
										part.width *= 10;
 | 
			
		||||
										part.width += fmt[i] - '0';
 | 
			
		||||
									}
 | 
			
		||||
									continue;
 | 
			
		||||
								} else if (fmt[i] == 'b' || fmt[i] == 'B') {
 | 
			
		||||
									part.type = FmtPart::INTEGER;
 | 
			
		||||
									part.base =  2;
 | 
			
		||||
								} else if (fmt[i] == 'o' || fmt[i] == 'O') {
 | 
			
		||||
									part.type = FmtPart::INTEGER;
 | 
			
		||||
									part.base =  8;
 | 
			
		||||
								} else if (fmt[i] == 'd' || fmt[i] == 'D') {
 | 
			
		||||
									part.type = FmtPart::INTEGER;
 | 
			
		||||
									part.base = 10;
 | 
			
		||||
								} else if (fmt[i] == 'h' || fmt[i] == 'H' ||
 | 
			
		||||
								           fmt[i] == 'x' || fmt[i] == 'X') {
 | 
			
		||||
									// hex digits always printed in lowercase for %h%x as well as %H%X
 | 
			
		||||
									part.type = FmtPart::INTEGER;
 | 
			
		||||
									part.base = 16;
 | 
			
		||||
								} else if (fmt[i] == 'c' || fmt[i] == 'C') {
 | 
			
		||||
									part.type = FmtPart::CHARACTER;
 | 
			
		||||
									part.sig.extend_u0(8);
 | 
			
		||||
									// %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog
 | 
			
		||||
								} else if (fmt[i] == 's' || fmt[i] == 'S') {
 | 
			
		||||
									part.type = FmtPart::CHARACTER;
 | 
			
		||||
									if ((part.sig.size() % 8) != 0)
 | 
			
		||||
										part.sig.extend_u0((part.sig.size() + 7) / 8 * 8);
 | 
			
		||||
									// %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog
 | 
			
		||||
									part.padding = ' ';
 | 
			
		||||
								} else {
 | 
			
		||||
									log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with unrecognized format character `%c' in argument %zu.\n", task_name.c_str(), fmt[i], fmtarg - args.begin() + 1);
 | 
			
		||||
								}
 | 
			
		||||
								break;
 | 
			
		||||
							}
 | 
			
		||||
							if (i == fmt.size()) {
 | 
			
		||||
								log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if (part.padding == '\0')
 | 
			
		||||
								part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' ';
 | 
			
		||||
 | 
			
		||||
							if (part.type == FmtPart::INTEGER) {
 | 
			
		||||
								if (part.base != 10 && part.plus) {
 | 
			
		||||
									log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1);
 | 
			
		||||
								}
 | 
			
		||||
								if (!has_leading_zero)
 | 
			
		||||
									apply_verilog_automatic_sizing(part);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							parts.push_back(part);
 | 
			
		||||
							part = {};
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if (!part.str.empty()) {
 | 
			
		||||
						part.type = FmtPart::STRING;
 | 
			
		||||
						parts.push_back(part);
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					FmtPart part = {};
 | 
			
		||||
					part.type = FmtPart::STRING;
 | 
			
		||||
					part.str = arg->str;
 | 
			
		||||
					parts.push_back(part);
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			default: log_abort();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<VerilogFmtArg> Fmt::emit_verilog() const
 | 
			
		||||
{
 | 
			
		||||
	std::vector<VerilogFmtArg> args;
 | 
			
		||||
	VerilogFmtArg fmt = {};
 | 
			
		||||
	fmt.type = VerilogFmtArg::STRING;
 | 
			
		||||
 | 
			
		||||
	for (auto &part : parts) {
 | 
			
		||||
		switch (part.type) {
 | 
			
		||||
			case FmtPart::STRING:
 | 
			
		||||
				for (char c : part.str) {
 | 
			
		||||
					if (c == '%')
 | 
			
		||||
						fmt.str += "%%";
 | 
			
		||||
					else
 | 
			
		||||
						fmt.str += c;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case FmtPart::INTEGER: {
 | 
			
		||||
				VerilogFmtArg arg = {};
 | 
			
		||||
				arg.type = VerilogFmtArg::INTEGER;
 | 
			
		||||
				arg.sig = part.sig;
 | 
			
		||||
				arg.signed_ = part.signed_;
 | 
			
		||||
				args.push_back(arg);
 | 
			
		||||
 | 
			
		||||
				fmt.str += '%';
 | 
			
		||||
				if (part.plus)
 | 
			
		||||
					fmt.str += '+';
 | 
			
		||||
				if (part.justify == FmtPart::LEFT)
 | 
			
		||||
					fmt.str += '-';
 | 
			
		||||
				if (part.width == 0) {
 | 
			
		||||
					if (!part.lzero)
 | 
			
		||||
						fmt.str += '0';
 | 
			
		||||
				} else if (part.width > 0) {
 | 
			
		||||
					log_assert(part.padding == ' ' || part.padding == '0');
 | 
			
		||||
					if (!part.lzero && part.base != 10)
 | 
			
		||||
						fmt.str += '0';
 | 
			
		||||
					else if (part.padding == '0')
 | 
			
		||||
						fmt.str += '0';
 | 
			
		||||
					fmt.str += std::to_string(part.width);
 | 
			
		||||
				}
 | 
			
		||||
				switch (part.base) {
 | 
			
		||||
					case  2: fmt.str += 'b'; break;
 | 
			
		||||
					case  8: fmt.str += 'o'; break;
 | 
			
		||||
					case 10: fmt.str += 'd'; break;
 | 
			
		||||
					case 16: fmt.str += 'h'; break;
 | 
			
		||||
					default: log_abort();
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case FmtPart::CHARACTER: {
 | 
			
		||||
				VerilogFmtArg arg;
 | 
			
		||||
				arg.type = VerilogFmtArg::INTEGER;
 | 
			
		||||
				arg.sig = part.sig;
 | 
			
		||||
				args.push_back(arg);
 | 
			
		||||
 | 
			
		||||
				fmt.str += '%';
 | 
			
		||||
				if (part.justify == FmtPart::LEFT)
 | 
			
		||||
					fmt.str += '-';
 | 
			
		||||
				if (part.sig.size() == 8) {
 | 
			
		||||
					if (part.width > 0) {
 | 
			
		||||
						log_assert(part.padding == '0' || part.padding == ' ');
 | 
			
		||||
						if (part.padding == '0')
 | 
			
		||||
							fmt.str += part.padding;
 | 
			
		||||
						fmt.str += std::to_string(part.width);
 | 
			
		||||
					}
 | 
			
		||||
					fmt.str += 'c';
 | 
			
		||||
				} else {
 | 
			
		||||
					log_assert(part.sig.size() % 8 == 0);
 | 
			
		||||
					if (part.width > 0) {
 | 
			
		||||
						log_assert(part.padding == ' '); // no zero padding
 | 
			
		||||
						fmt.str += std::to_string(part.width);
 | 
			
		||||
					}
 | 
			
		||||
					fmt.str += 's';
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args.insert(args.begin(), fmt);
 | 
			
		||||
	return args;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Fmt::render() const
 | 
			
		||||
{
 | 
			
		||||
	std::string str;
 | 
			
		||||
 | 
			
		||||
	for (auto &part : parts) {
 | 
			
		||||
		switch (part.type) {
 | 
			
		||||
			case FmtPart::STRING:
 | 
			
		||||
				str += part.str;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case FmtPart::INTEGER:
 | 
			
		||||
			case FmtPart::CHARACTER: {
 | 
			
		||||
				std::string buf;
 | 
			
		||||
				if (part.type == FmtPart::INTEGER) {
 | 
			
		||||
					RTLIL::Const value = part.sig.as_const();
 | 
			
		||||
 | 
			
		||||
					if (!part.lzero && part.base != 10) {
 | 
			
		||||
						size_t minimum_size = 0;
 | 
			
		||||
						for (size_t index = 0; index < (size_t)value.size(); index++)
 | 
			
		||||
							if (value[index] != State::S0)
 | 
			
		||||
								minimum_size = index + 1;
 | 
			
		||||
						value = value.extract(0, minimum_size);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (part.base == 2) {
 | 
			
		||||
						buf = value.as_string();
 | 
			
		||||
					} else if (part.base == 8 || part.base == 16) {
 | 
			
		||||
						size_t step = (part.base == 16) ? 4 : 3;
 | 
			
		||||
						for (size_t index = 0; index < (size_t)value.size(); index += step) {
 | 
			
		||||
							RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index));
 | 
			
		||||
							bool has_x = false, all_x = true, has_z = false, all_z = true;
 | 
			
		||||
							for (State bit : subvalue) {
 | 
			
		||||
								if (bit == State::Sx)
 | 
			
		||||
									has_x = true;
 | 
			
		||||
								else
 | 
			
		||||
									all_x = false;
 | 
			
		||||
								if (bit == State::Sz)
 | 
			
		||||
									has_z = true;
 | 
			
		||||
								else
 | 
			
		||||
									all_z = false;
 | 
			
		||||
							}
 | 
			
		||||
							if (all_x)
 | 
			
		||||
								buf += 'x';
 | 
			
		||||
							else if (all_z)
 | 
			
		||||
								buf += 'z';
 | 
			
		||||
							else if (has_x)
 | 
			
		||||
								buf += 'X';
 | 
			
		||||
							else if (has_z)
 | 
			
		||||
								buf += 'Z';
 | 
			
		||||
							else
 | 
			
		||||
								buf += "0123456789abcdef"[subvalue.as_int()];
 | 
			
		||||
						}
 | 
			
		||||
						std::reverse(buf.begin(), buf.end());
 | 
			
		||||
					} else if (part.base == 10) {
 | 
			
		||||
						bool has_x = false, all_x = true, has_z = false, all_z = true;
 | 
			
		||||
						for (State bit : value) {
 | 
			
		||||
							if (bit == State::Sx)
 | 
			
		||||
								has_x = true;
 | 
			
		||||
							else
 | 
			
		||||
								all_x = false;
 | 
			
		||||
							if (bit == State::Sz)
 | 
			
		||||
								has_z = true;
 | 
			
		||||
							else
 | 
			
		||||
								all_z = false;
 | 
			
		||||
						}
 | 
			
		||||
						if (all_x)
 | 
			
		||||
							buf += 'x';
 | 
			
		||||
						else if (all_z)
 | 
			
		||||
							buf += 'z';
 | 
			
		||||
						else if (has_x)
 | 
			
		||||
							buf += 'X';
 | 
			
		||||
						else if (has_z)
 | 
			
		||||
							buf += 'Z';
 | 
			
		||||
						else {
 | 
			
		||||
							bool negative = part.signed_ && value[value.size() - 1];
 | 
			
		||||
							RTLIL::Const absvalue;
 | 
			
		||||
							if (negative)
 | 
			
		||||
								absvalue = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1);
 | 
			
		||||
							else
 | 
			
		||||
								absvalue = value;
 | 
			
		||||
							log_assert(absvalue.is_fully_def());
 | 
			
		||||
							if (absvalue.is_fully_zero())
 | 
			
		||||
								buf += '0';
 | 
			
		||||
							while (!absvalue.is_fully_zero())	{
 | 
			
		||||
								buf += '0' + RTLIL::const_mod(absvalue, 10, part.signed_, false, 4).as_int();
 | 
			
		||||
								absvalue = RTLIL::const_div(absvalue, 10, part.signed_, false, absvalue.size());
 | 
			
		||||
							}
 | 
			
		||||
							if (negative || part.plus)
 | 
			
		||||
								buf += negative ? '-' : '+';
 | 
			
		||||
							std::reverse(buf.begin(), buf.end());
 | 
			
		||||
						}
 | 
			
		||||
					} else log_abort();
 | 
			
		||||
				} else if (part.type == FmtPart::CHARACTER) {
 | 
			
		||||
					buf = part.sig.as_const().decode_string();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				log_assert(part.width == 0 || part.padding != '\0');
 | 
			
		||||
				if (part.justify == FmtPart::RIGHT && buf.size() < part.width) {
 | 
			
		||||
					size_t pad_width = part.width - buf.size();
 | 
			
		||||
					if (part.padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
 | 
			
		||||
						str += buf.front();
 | 
			
		||||
						buf.erase(0, 1);
 | 
			
		||||
					}
 | 
			
		||||
					str += std::string(pad_width, part.padding);
 | 
			
		||||
				}
 | 
			
		||||
				str += buf;
 | 
			
		||||
				if (part.justify == FmtPart::LEFT && buf.size() < part.width)
 | 
			
		||||
					str += std::string(part.width - buf.size(), part.padding);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return str;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								kernel/fmt.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								kernel/fmt.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  yosys -- Yosys Open SYnthesis Suite
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2020  whitequark <whitequark@whitequark.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  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.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef FMT_H
 | 
			
		||||
#define FMT_H
 | 
			
		||||
 | 
			
		||||
#include "kernel/yosys.h"
 | 
			
		||||
 | 
			
		||||
YOSYS_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
// Verilog format argument, such as the arguments in:
 | 
			
		||||
//   $display("foo %d bar %01x", 4'b0, $signed(2'b11))
 | 
			
		||||
struct VerilogFmtArg {
 | 
			
		||||
	enum {
 | 
			
		||||
		STRING  = 0,
 | 
			
		||||
		INTEGER = 1,
 | 
			
		||||
	} type;
 | 
			
		||||
 | 
			
		||||
	// All types
 | 
			
		||||
	std::string filename;
 | 
			
		||||
	unsigned first_line;
 | 
			
		||||
 | 
			
		||||
	// STRING type
 | 
			
		||||
	std::string str;
 | 
			
		||||
 | 
			
		||||
	// INTEGER type
 | 
			
		||||
	RTLIL::SigSpec sig;
 | 
			
		||||
	bool signed_ = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// RTLIL format part, such as the substitutions in:
 | 
			
		||||
//   "foo {4: 4du} bar {2:01xs}"
 | 
			
		||||
struct FmtPart {
 | 
			
		||||
	enum {
 | 
			
		||||
		STRING  	= 0,
 | 
			
		||||
		INTEGER 	= 1,
 | 
			
		||||
		CHARACTER = 2,
 | 
			
		||||
	} type;
 | 
			
		||||
 | 
			
		||||
	// STRING type
 | 
			
		||||
	std::string str;
 | 
			
		||||
 | 
			
		||||
	// INTEGER/CHARACTER type
 | 
			
		||||
	RTLIL::SigSpec sig;
 | 
			
		||||
	enum {
 | 
			
		||||
		RIGHT	= 0,
 | 
			
		||||
		LEFT	= 1,
 | 
			
		||||
	} justify = RIGHT;
 | 
			
		||||
	char padding = '\0';
 | 
			
		||||
	size_t width = 0;
 | 
			
		||||
	// INTEGER type
 | 
			
		||||
	unsigned base = 10;
 | 
			
		||||
	bool signed_ = false;
 | 
			
		||||
	bool lzero = false;
 | 
			
		||||
	bool plus = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Fmt {
 | 
			
		||||
	std::vector<FmtPart> parts;
 | 
			
		||||
 | 
			
		||||
	void append_string(const std::string &str);
 | 
			
		||||
 | 
			
		||||
	void parse_rtlil(RTLIL::Cell *cell);
 | 
			
		||||
	void emit_rtlil(RTLIL::Cell *cell) const;
 | 
			
		||||
 | 
			
		||||
	void parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name);
 | 
			
		||||
	std::vector<VerilogFmtArg> emit_verilog() const;
 | 
			
		||||
 | 
			
		||||
	std::string render() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
YOSYS_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue