mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-19 02:22:39 +00:00
190 lines
6.1 KiB
C++
190 lines
6.1 KiB
C++
//
|
|
// ClockReceiver.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 22/07/2017.
|
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include "ForceInline.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <concepts>
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
|
|
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 <int ClockScale>
|
|
requires (is_power_of_two(ClockScale))
|
|
class Clocks {
|
|
public:
|
|
static constexpr int Scale = ClockScale;
|
|
static constexpr int Mask = ~(ClockScale - 1);
|
|
using IntType = int64_t;
|
|
|
|
constexpr Clocks(const IntType rhs) noexcept : length_(rhs * ClockScale) {}
|
|
constexpr Clocks() noexcept : length_(0) {}
|
|
|
|
// Assignments are implemented anywhere they can't lose data.
|
|
template <typename SourceClocks>
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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; }
|
|
|
|
constexpr Clocks operator +(const Clocks rhs) const { return Clocks(length_ + rhs.length_); }
|
|
constexpr Clocks operator -(const Clocks rhs) const { return Clocks(length_ - rhs.length_); }
|
|
|
|
constexpr Clocks operator *(const Clocks rhs) const { return Clocks(length_ * rhs.length_); }
|
|
constexpr Clocks operator /(const Clocks rhs) const { return Clocks(length_ / rhs.length_); }
|
|
|
|
constexpr Clocks operator %(const Clocks rhs) const { return Clocks(length_ % rhs.length_); }
|
|
constexpr Clocks operator &(const Clocks rhs) const { return Clocks(length_ & rhs.length_); }
|
|
|
|
constexpr Clocks operator -() const { return Clocks(-length_); }
|
|
|
|
auto operator <=>(const Clocks &) const = default;
|
|
|
|
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 <typename Type = IntType>
|
|
requires std::integral<Type> || std::floating_point<Type>
|
|
constexpr Type as() const {
|
|
const auto value = get();
|
|
|
|
if constexpr (sizeof(Type) == sizeof(IntType) && std::is_integral_v<Type>) {
|
|
if constexpr (std::is_same_v<Type, IntType>) {
|
|
return value;
|
|
} else if constexpr (std::is_signed_v<Type>) {
|
|
// Both integers are the same size, but a signed result is being asked for
|
|
// from an unsigned original.
|
|
return value > Type(std::numeric_limits<Type>::max()) ?
|
|
Type(std::numeric_limits<Type>::max()) : Type(value);
|
|
} else {
|
|
// An unsigned result is being asked for from a signed original.
|
|
return value < 0 ? 0 : Type(value);
|
|
}
|
|
}
|
|
|
|
return Type(std::clamp(length_, low<Type>, high<Type>));
|
|
}
|
|
|
|
/// @returns The underlying int, in its native form.
|
|
constexpr IntType get() const {
|
|
return length_ / ClockScale;
|
|
}
|
|
|
|
constexpr IntType raw() const {
|
|
return length_;
|
|
}
|
|
// operator int() is deliberately not provided, to avoid accidental subtitution of
|
|
// classes that use this template.
|
|
|
|
/*!
|
|
Caculates `*this / divisor`, converting that to `DestinationClocks`.
|
|
Sets `*this = *this % divisor`.
|
|
*/
|
|
// template <typename DestinationClocks = Clocks>
|
|
// 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 <typename DestinationClocks = Clocks>
|
|
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<IntType>::max()); }
|
|
static Clocks min() { return Clocks(std::numeric_limits<IntType>::min()); }
|
|
|
|
private:
|
|
IntType length_;
|
|
|
|
template <typename Type>
|
|
static consteval bool can_represent(const Type x) {
|
|
return std::numeric_limits<IntType>::min() <= x && std::numeric_limits<IntType>::max() >= x;
|
|
}
|
|
|
|
template<typename Type>
|
|
static constexpr IntType low =
|
|
can_represent(std::numeric_limits<Type>::min()) ?
|
|
IntType(std::numeric_limits<Type>::min()) : std::numeric_limits<IntType>::min();
|
|
|
|
template<typename Type>
|
|
static constexpr IntType high =
|
|
can_represent(std::numeric_limits<Type>::max()) ?
|
|
IntType(std::numeric_limits<Type>::max()) : std::numeric_limits<IntType>::max();
|
|
};
|
|
|
|
/// 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>;
|
|
|
|
/*!
|
|
Provides automated boilerplate for connecting an owner that works in one clock base to a receiver that works in another.
|
|
*/
|
|
template <typename TargetT, typename SourceClocks, typename DestinationClocks>
|
|
class ConvertedClockReceiver: public TargetT {
|
|
public:
|
|
using TargetT::TargetT;
|
|
|
|
void run_for(const SourceClocks duration) {
|
|
source_ += duration;
|
|
TargetT::run_for(source_.template flush<DestinationClocks>());
|
|
}
|
|
|
|
private:
|
|
SourceClocks source_;
|
|
};
|