diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index 2e386ef09..d0a9d811e 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -12,144 +12,94 @@ #include #include +#include #include #include -/* - Informal pattern for all classes that run from a clock cycle: - - Each will implement either or both of run_for(Cycles) and run_for(HalfCycles), as - is appropriate. - - Callers that are accumulating HalfCycles but want to talk to receivers that implement - only run_for(Cycles) can use HalfCycle.flush_cycles if they have appropriate storage, or - can wrap the receiver in HalfClockReceiver in order automatically to bind half-cycle - storage to it. - - Alignment rule: - - run_for(Cycles) may be called only after an even number of half cycles. E.g. the following - sequence will have undefined results: - - run_for(HalfCycles(1)) - run_for(Cycles(1)) - - An easy way to ensure this as a caller is to pick only one of run_for(Cycles) and - run_for(HalfCycles) to use. - - Reasoning: - - Users of this template may with to implement run_for(Cycles) and run_for(HalfCycles) - where there is a need to implement at half-cycle precision but a faster execution - path can be offered for full-cycle precision. Those users are permitted to assume - phase in run_for(Cycles) and should do so to be compatible with callers that use - only run_for(Cycles). - - Corollary: - - Starting from nothing, the first run_for(HalfCycles(1)) will do the **first** half - of a full cycle. The second will do the second half. Etc. - -*/ +constexpr bool is_power_of_two(const int v) { + return !(v & (v - 1)); +} /*! Provides a class that wraps a plain int, providing most of the basic arithmetic and Boolean operators, but forcing callers and receivers to be explicit as to usage. */ -template class WrappedInt { +template +requires (is_power_of_two(ClockScale)) +class Clocks { public: + static constexpr int Scale = ClockScale; + static constexpr int Mask = ~(ClockScale - 1); using IntType = int64_t; - forceinline constexpr WrappedInt(IntType l) noexcept : length_(l) {} - forceinline constexpr WrappedInt() noexcept : length_(0) {} + constexpr Clocks(const IntType rhs) noexcept : length_(rhs * ClockScale) {} + constexpr Clocks() noexcept : length_(0) {} - forceinline T &operator =(const T &rhs) { - length_ = rhs.length_; + // Assignments are implemented anywhere they can't lose data. + template + requires (SourceClocks::Mask <= Mask) + constexpr Clocks(const SourceClocks rhs) noexcept : length_(rhs.raw() & Mask) {} + + Clocks operator =(const Clocks rhs) { + length_ = rhs.length_ & Mask; return *this; } - forceinline T &operator +=(const T &rhs) { - length_ += rhs.length_; - return *static_cast(this); + Clocks operator +=(const Clocks rhs) { length_ += rhs.length_; return *this; } + Clocks operator -=(const Clocks rhs) { length_ -= rhs.length_; return *this; } + Clocks operator ++() { length_ += ClockScale; return *this; } + Clocks operator --() { length_ -= ClockScale; return *this; } + + Clocks operator ++(int) { + const Clocks result = *this; + length_ += ClockScale; + return result; + } + Clocks operator --(int) { + const Clocks result = *this; + length_ -= ClockScale; + return result; } - forceinline T &operator -=(const T &rhs) { - length_ -= rhs.length_; - return *static_cast(this); - } + Clocks operator *=(const Clocks rhs) { *this = Clocks(length_ * rhs.length_); return *this; } + Clocks operator /=(const Clocks rhs) { *this = Clocks(length_ / rhs.length_); return *this; } + Clocks operator %=(const Clocks rhs) { *this = Clocks(length_ % rhs.length_); return *this; } + Clocks operator &=(const Clocks rhs) { *this = Clocks(length_ & rhs.length_); return *this; } - forceinline T &operator ++() { - ++ length_; - return *static_cast(this); - } + constexpr Clocks operator +(const Clocks rhs) const { return Clocks(length_ + rhs.length_); } + constexpr Clocks operator -(const Clocks rhs) const { return Clocks(length_ - rhs.length_); } - forceinline T &operator ++(int) { - length_ ++; - return *static_cast(this); - } + constexpr Clocks operator *(const Clocks rhs) const { return Clocks(length_ * rhs.length_); } + constexpr Clocks operator /(const Clocks rhs) const { return Clocks(length_ / rhs.length_); } - forceinline T &operator --() { - -- length_; - return *static_cast(this); - } + constexpr Clocks operator %(const Clocks rhs) const { return Clocks(length_ % rhs.length_); } + constexpr Clocks operator &(const Clocks rhs) const { return Clocks(length_ & rhs.length_); } - forceinline T &operator --(int) { - length_ --; - return *static_cast(this); - } + constexpr Clocks operator -() const { return Clocks(-length_); } - forceinline T &operator *=(const T &rhs) { - length_ *= rhs.length_; - return *static_cast(this); - } + auto operator <=>(const Clocks &) const = default; - forceinline T &operator /=(const T &rhs) { - length_ /= rhs.length_; - return *static_cast(this); - } - - forceinline T &operator %=(const T &rhs) { - length_ %= rhs.length_; - return *static_cast(this); - } - - forceinline T &operator &=(const T &rhs) { - length_ &= rhs.length_; - return *static_cast(this); - } - - forceinline constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); } - forceinline constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); } - - forceinline constexpr T operator *(const T &rhs) const { return T(length_ * rhs.length_); } - forceinline constexpr T operator /(const T &rhs) const { return T(length_ / rhs.length_); } - - forceinline constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); } - forceinline constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); } - - forceinline constexpr T operator -() const { return T(- length_); } - - auto operator <=>(const WrappedInt &) const = default; - - forceinline constexpr bool operator !() const { return !length_; } + constexpr bool operator !() const { return !length_; } // bool operator () is not supported because it offers an implicit cast to int, // which is prone silently to permit misuse. /// @returns The underlying int, converted to a numeric type of your choosing, clamped to that type's range. - template + template requires std::integral || std::floating_point constexpr Type as() const { + const auto value = get(); + if constexpr (sizeof(Type) == sizeof(IntType) && std::is_integral_v) { if constexpr (std::is_same_v) { - return length_; + return value; } else if constexpr (std::is_signed_v) { // Both integers are the same size, but a signed result is being asked for // from an unsigned original. - return length_ > Type(std::numeric_limits::max()) ? - Type(std::numeric_limits::max()) : Type(length_); + return value > Type(std::numeric_limits::max()) ? + Type(std::numeric_limits::max()) : Type(value); } else { // An unsigned result is being asked for from a signed original. - return length_ < 0 ? 0 : Type(length_); + return value < 0 ? 0 : Type(value); } } @@ -157,37 +107,49 @@ public: } /// @returns The underlying int, in its native form. - forceinline constexpr IntType as_integral() const { return length_; } - - /*! - Severs from @c this the effect of dividing by @c divisor; @c this will end up with - the value of @c this modulo @c divisor and @c divided by @c divisor is returned. - */ - template forceinline Result divide(const T &divisor) { - Result r; - static_cast(this)->fill(r, divisor); - return r; + constexpr IntType get() const { + return length_ / ClockScale; } - /*! - Flushes the value in @c this. The current value is returned, and the internal value - is reset to zero. - */ - template Result flush() { - // Jiggery pokery here; switching to function overloading avoids - // the namespace-level requirement for template specialisation. - Result r; - static_cast(this)->fill(r); - return r; + constexpr IntType raw() const { + return length_; } - // operator int() is deliberately not provided, to avoid accidental subtitution of // classes that use this template. -protected: - IntType length_; + /*! + Caculates `*this / divisor`, converting that to `DestinationClocks`. + Sets `*this = *this % divisor`. + */ +// template +// DestinationClocks divide(const DestinationClocks divisor) { +// // +// +// +// Clocks result; +// result.length_ = length_ / divisor.length_; +// length_ %= divisor.length_; +// return result; +// } + + /*! + Extracts a whole number of `DestinationClock`s from `*this`. + Leaves the residue here. + */ + template + requires (DestinationClocks::Mask <= Mask) + DestinationClocks flush() { + const auto result = DestinationClocks(length_); + length_ &= Mask ^ DestinationClocks::Mask; + return result; + } + + static Clocks max() { return Clocks(std::numeric_limits::max()); } + static Clocks min() { return Clocks(std::numeric_limits::min()); } private: + IntType length_; + template static consteval bool can_represent(const Type x) { return std::numeric_limits::min() <= x && std::numeric_limits::max() >= x; @@ -204,104 +166,24 @@ private: IntType(std::numeric_limits::max()) : std::numeric_limits::max(); }; -/// Describes an integer number of whole cycles: pairs of clock signal transitions. -class Cycles: public WrappedInt { -public: - forceinline constexpr Cycles(IntType l) noexcept : WrappedInt(l) {} - forceinline constexpr Cycles() noexcept : WrappedInt() {} - forceinline static constexpr Cycles max() { - return Cycles(std::numeric_limits::max()); - } - -private: - friend WrappedInt; - void fill(Cycles &result) { - result.length_ = length_; - length_ = 0; - } - - void fill(Cycles &result, const Cycles &divisor) { - result.length_ = length_ / divisor.length_; - length_ %= divisor.length_; - } -}; - -/// Describes an integer number of half cycles: single clock signal transitions. -class HalfCycles: public WrappedInt { -public: - forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt(l) {} - forceinline constexpr HalfCycles() noexcept : WrappedInt() {} - forceinline static constexpr HalfCycles max() { - return HalfCycles(std::numeric_limits::max()); - } - - forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : - WrappedInt(cycles.as_integral() * 2) {} - - /// @returns The number of whole cycles completely covered by this span of half cycles. - forceinline constexpr Cycles cycles() const { - return Cycles(length_ >> 1); - } - - /*! - Severs from @c this the effect of dividing by @c divisor; @c this will end up with - the value of @c this modulo @c divisor . @c this divided by @c divisor is returned. - */ - forceinline Cycles divide_cycles(const Cycles &divisor) { - const HalfCycles half_divisor = HalfCycles(divisor); - const Cycles result(length_ / half_divisor.length_); - length_ %= half_divisor.length_; - return result; - } - - /*! - Equivalent to @c divide_cycles(Cycles(1)) but faster. - */ - forceinline Cycles divide_cycles() { - const Cycles result(length_ >> 1); - length_ &= 1; - return result; - } - -private: - friend WrappedInt; - void fill(Cycles &result) { - result = Cycles(length_ >> 1); - length_ &= 1; - } - - void fill(HalfCycles &result) { - result.length_ = length_; - length_ = 0; - } - - void fill(Cycles &result, const HalfCycles &divisor) { - result = Cycles(length_ / (divisor.length_ << 1)); - length_ %= (divisor.length_ << 1); - } - - void fill(HalfCycles &result, const HalfCycles &divisor) { - result.length_ = length_ / divisor.length_; - length_ %= divisor.length_; - } -}; - -// Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles -// without losing the fractional part. +/// Reasons Clocks into being a count of querter cycles, building half- and whole-cycles from there. +using Cycles = Clocks<4>; +using HalfCycles = Clocks<2>; +using QuarterCycles = Clocks<1>; /*! - If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver - automatically to gain run_for(HalfCycles). + Provides automated boilerplate for connecting an owner that works in one clock base to a receiver that works in another. */ -template class HalfClockReceiver: public T { +template +class ConvertedClockReceiver: public TargetT { public: - using T::T; + using TargetT::TargetT; - forceinline void run_for(const HalfCycles half_cycles) { - half_cycles_ += half_cycles; - T::run_for(half_cycles_.flush()); + void run_for(const SourceClocks duration) { + source_ += duration; + TargetT::run_for(source_.template flush()); } private: - HalfCycles half_cycles_; + SourceClocks source_; };