mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	cxxrtl: add a representation of simulation timestamps.
While the VCD format separates the timescale and the timestep (likely to allow representing the timestep with a small integer type), time in CXXRTL is represented using a uniform 96-bit number, which allows for a ±100 year range at femtosecond resolution. The implementation uses `value<96>`, which provides fast arithmetic and comparison operations, as well as conversion to/from a more common representation of integer seconds plus femtoseconds.
This commit is contained in:
		
							parent
							
								
									c72dc15f02
								
							
						
					
					
						commit
						a94fafa8fe
					
				
					 1 changed files with 229 additions and 0 deletions
				
			
		
							
								
								
									
										229
									
								
								backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,229 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2023  Catherine <whitequark@whitequark.org> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted. | ||||
|  * | ||||
|  *  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 CXXRTL_TIME_H | ||||
| #define CXXRTL_TIME_H | ||||
| 
 | ||||
| #include <cinttypes> | ||||
| #include <string> | ||||
| 
 | ||||
| #include <cxxrtl/cxxrtl.h> | ||||
| 
 | ||||
| namespace cxxrtl { | ||||
| 
 | ||||
| // A timestamp or a difference in time, stored as a 96-bit number of femtoseconds (10e-15 s). The dynamic range and
 | ||||
| // resolution of this format can represent any VCD timestamp within 136 years, without the need for a timescale.
 | ||||
| class time { | ||||
| public: | ||||
| 	static constexpr size_t bits = 96; // 3 chunks
 | ||||
| 
 | ||||
| private: | ||||
| 	static constexpr value<bits> resolution = value<bits> { | ||||
| 		chunk_t(1000000000000000ull & 0xffffffffull), chunk_t(1000000000000000ull >> 32), 0u | ||||
| 	}; | ||||
| 	static constexpr size_t resolution_digits = 15; | ||||
| 
 | ||||
| 	// Signed number of femtoseconds from the beginning of time.
 | ||||
| 	value<bits> raw; | ||||
| 
 | ||||
| public: | ||||
| 	constexpr time() {} | ||||
| 
 | ||||
| 	explicit constexpr time(const value<bits> &raw) : raw(raw) {} | ||||
| 	explicit operator const value<bits> &() const { return raw; } | ||||
| 
 | ||||
| 	static constexpr time maximum() { | ||||
| 		return time(value<bits> { 0xffffffffu, 0xffffffffu, 0x7fffffffu }); | ||||
| 	} | ||||
| 
 | ||||
| 	time(int32_t secs, int64_t femtos) { | ||||
| 		value<32> secs_val; | ||||
| 		secs_val.set((uint32_t)secs); | ||||
| 		value<64> femtos_val; | ||||
| 		femtos_val.set((uint64_t)femtos); | ||||
| 		raw = secs_val.sext<bits>().mul<bits>(resolution).add(femtos_val.sext<bits>()); | ||||
| 	} | ||||
| 
 | ||||
| 	bool is_zero() const { | ||||
| 		return raw.is_zero(); | ||||
| 	} | ||||
| 
 | ||||
| 	// Extracts the sign of the value.
 | ||||
| 	bool is_negative() const { | ||||
| 		return raw.is_neg(); | ||||
| 	} | ||||
| 
 | ||||
| 	// Extracts the absolute number of whole seconds. Negative if the value is negative.
 | ||||
| 	int32_t secs() const { | ||||
| 		return raw.sdivmod(resolution).first.trunc<32>().get<uint32_t>(); | ||||
| 	} | ||||
| 
 | ||||
| 	// Extracts the absolute number of femtoseconds in the fractional second. Negative if the value is negative.
 | ||||
| 	int64_t femtos() const { | ||||
| 		return raw.sdivmod(resolution).second.trunc<64>().get<uint64_t>(); | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator==(const time &other) const { | ||||
| 		return raw == other.raw; | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator!=(const time &other) const { | ||||
| 		return raw != other.raw; | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator>(const time &other) const { | ||||
| 		return other.raw.scmp(raw); | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator>=(const time &other) const { | ||||
| 		return !raw.scmp(other.raw); | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator<(const time &other) const { | ||||
| 		return raw.scmp(other.raw); | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator<=(const time &other) const { | ||||
| 		return !other.raw.scmp(raw); | ||||
| 	} | ||||
| 
 | ||||
| 	time operator+(const time &other) const { | ||||
| 		return time(raw.add(other.raw)); | ||||
| 	} | ||||
| 
 | ||||
| 	time &operator+=(const time &other) { | ||||
| 		*this = *this + other; | ||||
| 		return *this; | ||||
| 	} | ||||
| 
 | ||||
| 	time operator-() const { | ||||
| 		return time(raw.neg()); | ||||
| 	} | ||||
| 
 | ||||
| 	time operator-(const time &other) const { | ||||
| 		return *this + (-other); | ||||
| 	} | ||||
| 
 | ||||
| 	time &operator-=(const time &other) { | ||||
| 		*this = *this - other; | ||||
| 		return *this; | ||||
| 	} | ||||
| 
 | ||||
| 	operator std::string() const { | ||||
| 		char buf[38]; // len(f"-{2**64}.{10**15-1}") + 1 == 38
 | ||||
| 		int32_t secs = this->secs(); | ||||
| 		int64_t femtos = this->femtos(); | ||||
| 		snprintf(buf, sizeof(buf), "%s%" PRIi32 ".%015" PRIi64, | ||||
| 			is_negative() ? "-" : "", secs >= 0 ? secs : -secs, femtos >= 0 ? femtos : -femtos); | ||||
| 		return buf; | ||||
| 	} | ||||
| 
 | ||||
| #if __cplusplus >= 201603L | ||||
| 	[[nodiscard("ignoring parse errors")]] | ||||
| #endif | ||||
| 	bool parse(const std::string &str) { | ||||
| 		enum { | ||||
| 			parse_sign_opt, | ||||
| 			parse_integral, | ||||
| 			parse_fractional, | ||||
| 		} state = parse_sign_opt; | ||||
| 		bool negative = false; | ||||
| 		int32_t integral = 0; | ||||
| 		int64_t fractional = 0; | ||||
| 		size_t frac_digits = 0; | ||||
| 		for (auto chr : str) { | ||||
| 			switch (state) { | ||||
| 				case parse_sign_opt: | ||||
| 					state = parse_integral; | ||||
| 					if (chr == '+' || chr == '-') { | ||||
| 						negative = (chr == '-'); | ||||
| 						break; | ||||
| 					} | ||||
| 					/* fallthrough */ | ||||
| 				case parse_integral: | ||||
| 					if (chr >= '0' && chr <= '9') { | ||||
| 						integral *= 10; | ||||
| 						integral += chr - '0'; | ||||
| 					} else if (chr == '.') { | ||||
| 						state = parse_fractional; | ||||
| 					} else { | ||||
| 						return false; | ||||
| 					} | ||||
| 					break; | ||||
| 				case parse_fractional: | ||||
| 					if (chr >= '0' && chr <= '9' && frac_digits < resolution_digits) { | ||||
| 						fractional *= 10; | ||||
| 						fractional += chr - '0'; | ||||
| 						frac_digits++; | ||||
| 					} else { | ||||
| 						return false; | ||||
| 					} | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (frac_digits == 0) | ||||
| 			return false; | ||||
| 		while (frac_digits++ < resolution_digits) | ||||
| 			fractional *= 10; | ||||
| 		*this = negative ? -time { integral, fractional} : time { integral, fractional }; | ||||
| 		return true; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // Out-of-line definition required until C++17.
 | ||||
| constexpr value<time::bits> time::resolution; | ||||
| 
 | ||||
| std::ostream &operator<<(std::ostream &os, const time &val) { | ||||
| 	os << (std::string)val; | ||||
| 	return os; | ||||
| } | ||||
| 
 | ||||
| // These literals are (confusingly) compatible with the ones from `std::chrono`: the `std::chrono` literals do not
 | ||||
| // have an underscore (e.g. 1ms) and the `cxxrtl::time` literals do (e.g. 1_ms). This syntactic difference is
 | ||||
| // a requirement of the C++ standard. Despite being compatible the literals should not be mixed in the same namespace.
 | ||||
| namespace time_literals { | ||||
| 
 | ||||
| time operator""_s(unsigned long long seconds) { | ||||
| 	return time { (int32_t)seconds, 0 }; | ||||
| } | ||||
| 
 | ||||
| time operator""_ms(unsigned long long milliseconds) { | ||||
| 	return time { 0, (int64_t)milliseconds * 1000000000000 }; | ||||
| } | ||||
| 
 | ||||
| time operator""_us(unsigned long long microseconds) { | ||||
| 	return time { 0, (int64_t)microseconds * 1000000000 }; | ||||
| } | ||||
| 
 | ||||
| time operator""_ns(unsigned long long nanoseconds) { | ||||
| 	return time { 0, (int64_t)nanoseconds * 1000000 }; | ||||
| } | ||||
| 
 | ||||
| time operator""_ps(unsigned long long picoseconds) { | ||||
| 	return time { 0, (int64_t)picoseconds * 1000 }; | ||||
| } | ||||
| 
 | ||||
| time operator""_fs(unsigned long long femtoseconds) { | ||||
| 	return time { 0, (int64_t)femtoseconds }; | ||||
| } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue