diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h new file mode 100644 index 00000000000..51f59321e2d --- /dev/null +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h @@ -0,0 +1,229 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2023 Catherine + * + * 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 +#include + +#include + +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 resolution = value { + 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 raw; + +public: + constexpr time() {} + + explicit constexpr time(const value &raw) : raw(raw) {} + explicit operator const value &() const { return raw; } + + static constexpr time maximum() { + return time(value { 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().mul(resolution).add(femtos_val.sext()); + } + + 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(); + } + + // 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(); + } + + 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::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