mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	cxxrtl: refactor the formatter and use a closure.
This commit achieves three roughly equally important goals: 1. To bring the rendering code in kernel/fmt.cc and in cxxrtl.h as close together as possible, with an ideal of only having the bigint library as the difference between the render functions. 2. To make the treatment of `$time` and `$realtime` in CXXRTL closer to the Verilog semantics, at least in the formatting code. 3. To change the code generator so that all of the `$print`-to-`string` conversion code is contained inside of a closure. There are two reasons to aim for goal (3): a. Because output redirection through definition of a global ostream object is neither convenient nor useful for environments where the output is consumed by other code rather than being printed on a terminal. b. Because it may be desirable to, in some cases, ignore the `$print` cells that are present in the netlist based on a runtime decision. This is doubly true for an upcoming `$check` cell implementing assertions, since failing a `$check` would by default cause a crash.
This commit is contained in:
		
							parent
							
								
									bf1a99da09
								
							
						
					
					
						commit
						a33acb7cd9
					
				
					 5 changed files with 192 additions and 173 deletions
				
			
		|  | @ -565,7 +565,7 @@ struct value : public expr_base<value<Bits>> { | |||
| 	} | ||||
| 
 | ||||
| 	value<Bits> neg() const { | ||||
| 		return value<Bits> { 0u }.sub(*this); | ||||
| 		return value<Bits>().sub(*this); | ||||
| 	} | ||||
| 
 | ||||
| 	bool ucmp(const value<Bits> &other) const { | ||||
|  | @ -763,102 +763,134 @@ std::ostream &operator<<(std::ostream &os, const value<Bits> &val) { | |||
| 	return os; | ||||
| } | ||||
| 
 | ||||
| template<size_t Bits> | ||||
| struct value_formatted { | ||||
| 	const value<Bits> &val; | ||||
| 	bool character; | ||||
| 	bool justify_left; | ||||
| 	char padding; | ||||
| 	int width; | ||||
| 	int base; | ||||
| 	bool signed_; | ||||
| 	bool plus; | ||||
| // Must be kept in sync with `struct FmtPart` in kernel/fmt.h!
 | ||||
| // Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
 | ||||
| struct fmt_part { | ||||
| 	enum { | ||||
| 		STRING  	= 0, | ||||
| 		INTEGER 	= 1, | ||||
| 		CHARACTER = 2, | ||||
| 		TIME    	= 3, | ||||
| 	} type; | ||||
| 
 | ||||
| 	value_formatted(const value<Bits> &val, bool character, bool justify_left, char padding, int width, int base, bool signed_, bool plus) : | ||||
| 		val(val), character(character), justify_left(justify_left), padding(padding), width(width), base(base), signed_(signed_), plus(plus) {} | ||||
| 	value_formatted(const value_formatted<Bits> &) = delete; | ||||
| 	value_formatted<Bits> &operator=(const value_formatted<Bits> &rhs) = delete; | ||||
| 	// STRING type
 | ||||
| 	std::string str; | ||||
| 
 | ||||
| 	// INTEGER/CHARACTER types
 | ||||
| 	// + value<Bits> val;
 | ||||
| 
 | ||||
| 	// INTEGER/CHARACTER/TIME types
 | ||||
| 	enum { | ||||
| 		RIGHT	= 0, | ||||
| 		LEFT	= 1, | ||||
| 	} justify; // = RIGHT;
 | ||||
| 	char padding; // = '\0';
 | ||||
| 	size_t width; // = 0;
 | ||||
| 
 | ||||
| 	// INTEGER type
 | ||||
| 	unsigned base; // = 10;
 | ||||
| 	bool signed_; // = false;
 | ||||
| 	bool plus; // = false;
 | ||||
| 
 | ||||
| 	// TIME type
 | ||||
| 	bool realtime; // = false;
 | ||||
| 	// + int64_t itime;
 | ||||
| 	// + double ftime;
 | ||||
| 
 | ||||
| 	// Format the part as a string.
 | ||||
| 	//
 | ||||
| 	// The values of `itime` and `ftime` are used for `$time` and `$realtime`, correspondingly.
 | ||||
| 	template<size_t Bits> | ||||
| 	std::string render(value<Bits> val, int64_t itime, double ftime) | ||||
| 	{ | ||||
| 		// We might want to replace some of these bit() calls with direct
 | ||||
| 		// chunk access if it turns out to be slow enough to matter.
 | ||||
| 		std::string buf; | ||||
| 		switch (type) { | ||||
| 			case STRING: | ||||
| 				return str; | ||||
| 
 | ||||
| 			case CHARACTER: { | ||||
| 				buf.reserve(Bits/8); | ||||
| 				for (int i = 0; i < Bits; i += 8) { | ||||
| 					char ch = 0; | ||||
| 					for (int j = 0; j < 8 && i + j < int(Bits); j++) | ||||
| 						if (val.bit(i + j)) | ||||
| 							ch |= 1 << j; | ||||
| 					if (ch != 0) | ||||
| 						buf.append({ch}); | ||||
| 				} | ||||
| 				std::reverse(buf.begin(), buf.end()); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			case INTEGER: { | ||||
| 				size_t width = Bits; | ||||
| 				if (base != 10) { | ||||
| 					width = 0; | ||||
| 					for (size_t index = 0; index < Bits; index++) | ||||
| 						if (val.bit(index)) | ||||
| 							width = index + 1; | ||||
| 				} | ||||
| 
 | ||||
| 				if (base == 2) { | ||||
| 					for (size_t i = width; i > 0; i--) | ||||
| 						buf += (val.bit(i - 1) ? '1' : '0'); | ||||
| 				} else if (base == 8 || base == 16) { | ||||
| 					size_t step = (base == 16) ? 4 : 3; | ||||
| 					for (size_t index = 0; index < width; index += step) { | ||||
| 						uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); | ||||
| 						if (step == 4) | ||||
| 							value |= val.bit(index + 3) << 3; | ||||
| 						buf += "0123456789abcdef"[value]; | ||||
| 					} | ||||
| 					std::reverse(buf.begin(), buf.end()); | ||||
| 				} else if (base == 10) { | ||||
| 					bool negative = signed_ && val.is_neg(); | ||||
| 					if (negative) | ||||
| 						val = val.neg(); | ||||
| 					if (val.is_zero()) | ||||
| 						buf += '0'; | ||||
| 					value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); | ||||
| 					while (!xval.is_zero()) { | ||||
| 						value<(Bits > 4 ? Bits : 4)> quotient, remainder; | ||||
| 						if (Bits >= 4) | ||||
| 							std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); | ||||
| 						else | ||||
| 							std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); | ||||
| 						buf += '0' + remainder.template trunc<4>().template get<uint8_t>(); | ||||
| 						xval = quotient; | ||||
| 					} | ||||
| 					if (negative || plus) | ||||
| 						buf += negative ? '-' : '+'; | ||||
| 					std::reverse(buf.begin(), buf.end()); | ||||
| 				} else assert(false && "Unsupported base for fmt_part"); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			case TIME: { | ||||
| 				buf = realtime ? std::to_string(ftime) : std::to_string(itime); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		std::string str; | ||||
| 		assert(width == 0 || padding != '\0'); | ||||
| 		if (justify == RIGHT && buf.size() < width) { | ||||
| 			size_t pad_width = width - buf.size(); | ||||
| 			if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { | ||||
| 				str += buf.front(); | ||||
| 				buf.erase(0, 1); | ||||
| 			} | ||||
| 			str += std::string(pad_width, padding); | ||||
| 		} | ||||
| 		str += buf; | ||||
| 		if (justify == LEFT && buf.size() < width) | ||||
| 			str += std::string(width - buf.size(), padding); | ||||
| 		return str; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| template<size_t Bits> | ||||
| std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf) | ||||
| { | ||||
| 	value<Bits> val = vf.val; | ||||
| 
 | ||||
| 	std::string buf; | ||||
| 
 | ||||
| 	// We might want to replace some of these bit() calls with direct
 | ||||
| 	// chunk access if it turns out to be slow enough to matter.
 | ||||
| 
 | ||||
| 	if (!vf.character) { | ||||
| 		size_t width = Bits; | ||||
| 		if (vf.base != 10) { | ||||
| 			width = 0; | ||||
| 			for (size_t index = 0; index < Bits; index++) | ||||
| 				if (val.bit(index)) | ||||
| 					width = index + 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (vf.base == 2) { | ||||
| 			for (size_t i = width; i > 0; i--) | ||||
| 				buf += (val.bit(i - 1) ? '1' : '0'); | ||||
| 		} else if (vf.base == 8 || vf.base == 16) { | ||||
| 			size_t step = (vf.base == 16) ? 4 : 3; | ||||
| 			for (size_t index = 0; index < width; index += step) { | ||||
| 				uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); | ||||
| 				if (step == 4) | ||||
| 					value |= val.bit(index + 3) << 3; | ||||
| 				buf += "0123456789abcdef"[value]; | ||||
| 			} | ||||
| 			std::reverse(buf.begin(), buf.end()); | ||||
| 		} else if (vf.base == 10) { | ||||
| 			bool negative = vf.signed_ && val.is_neg(); | ||||
| 			if (negative) | ||||
| 				val = val.neg(); | ||||
| 			if (val.is_zero()) | ||||
| 				buf += '0'; | ||||
| 			while (!val.is_zero()) { | ||||
| 				value<Bits> quotient, remainder; | ||||
| 				if (Bits >= 4) | ||||
| 					std::tie(quotient, remainder) = val.udivmod(value<Bits>{10u}); | ||||
| 				else | ||||
| 					std::tie(quotient, remainder) = std::make_pair(value<Bits>{0u}, val); | ||||
| 				buf += '0' + remainder.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get<uint8_t>(); | ||||
| 				val = quotient; | ||||
| 			} | ||||
| 			if (negative || vf.plus) | ||||
| 				buf += negative ? '-' : '+'; | ||||
| 			std::reverse(buf.begin(), buf.end()); | ||||
| 		} else assert(false); | ||||
| 	} else { | ||||
| 		buf.reserve(Bits/8); | ||||
| 		for (int i = 0; i < Bits; i += 8) { | ||||
| 			char ch = 0; | ||||
| 			for (int j = 0; j < 8 && i + j < int(Bits); j++) | ||||
| 				if (val.bit(i + j)) | ||||
| 					ch |= 1 << j; | ||||
| 			if (ch != 0) | ||||
| 				buf.append({ch}); | ||||
| 		} | ||||
| 		std::reverse(buf.begin(), buf.end()); | ||||
| 	} | ||||
| 
 | ||||
| 	assert(vf.width == 0 || vf.padding != '\0'); | ||||
| 	if (!vf.justify_left && buf.size() < vf.width) { | ||||
| 		size_t pad_width = vf.width - buf.size(); | ||||
| 		if (vf.padding == '0' && (buf.front() == '+' || buf.front() == '-')) { | ||||
| 			os << buf.front(); | ||||
| 			buf.erase(0, 1); | ||||
| 		} | ||||
| 		os << std::string(pad_width, vf.padding); | ||||
| 	} | ||||
| 	os << buf; | ||||
| 	if (vf.justify_left && buf.size() < vf.width) | ||||
| 		os << std::string(vf.width - buf.size(), vf.padding); | ||||
| 
 | ||||
| 	return os; | ||||
| } | ||||
| 
 | ||||
| // An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
 | ||||
| // the simulation.
 | ||||
| struct observer { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue