feat(wire & register): implement these 2 components
This commit is contained in:
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#ifdef _DEBUG
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#endif
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
|
|
||||||
namespace dark::debug {
|
namespace dark::debug {
|
||||||
|
@ -1,144 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "debug.h"
|
|
||||||
#include "concept.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <concepts>
|
|
||||||
|
|
||||||
namespace dark::hardware {
|
|
||||||
|
|
||||||
struct Visitor;
|
|
||||||
struct Wire;
|
|
||||||
struct Register;
|
|
||||||
|
|
||||||
template <typename _Fn>
|
|
||||||
concept WireFunction =
|
|
||||||
!std::same_as <Wire, std::decay_t <_Fn>> &&
|
|
||||||
requires(_Fn &&__f) { { __f() } -> std::convertible_to <max_size_t>; };
|
|
||||||
|
|
||||||
struct WireBase {
|
|
||||||
using _Ret_t = int;
|
|
||||||
using _Cpy_t = WireBase *;
|
|
||||||
virtual _Ret_t call() const = 0;
|
|
||||||
virtual ~WireBase() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <WireFunction _Fn>
|
|
||||||
struct WireImpl final : WireBase {
|
|
||||||
_Fn _M_lambda;
|
|
||||||
|
|
||||||
template <typename _Fn2>
|
|
||||||
WireImpl(_Fn2 &&fn) : _M_lambda(std::forward <_Fn2>(fn)) {}
|
|
||||||
|
|
||||||
_Ret_t call() const override { return this->_M_lambda(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EmptyWire final : WireBase {
|
|
||||||
_Ret_t call() const override {
|
|
||||||
debug::assert(false, "Empty wire is called.");
|
|
||||||
debug::unreachable();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Wire {
|
|
||||||
private:
|
|
||||||
friend struct Visitor;
|
|
||||||
|
|
||||||
using _Manage_t = WireBase;
|
|
||||||
|
|
||||||
std::unique_ptr <_Manage_t> _M_impl;
|
|
||||||
mutable max_size_t _M_cache;
|
|
||||||
mutable bool _M_holds;
|
|
||||||
|
|
||||||
[[no_unique_address]]
|
|
||||||
debug::DebugValue <bool, false> _M_dirty; // Can be assigned only once
|
|
||||||
|
|
||||||
void sync() { this->_M_holds = false; }
|
|
||||||
|
|
||||||
template <WireFunction _Fn>
|
|
||||||
static auto _M_new_impl(_Fn &&fn) -> _Manage_t * {
|
|
||||||
return new WireImpl <std::decay_t<_Fn>> {std::forward <_Fn>(fn)};
|
|
||||||
}
|
|
||||||
|
|
||||||
void _M_check_double_assign() {
|
|
||||||
debug::assert(!this->_M_dirty, "Wire is already assigned.");
|
|
||||||
this->_M_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Wire() : _M_impl(new EmptyWire), _M_cache(), _M_holds(), _M_dirty() {}
|
|
||||||
|
|
||||||
Wire(Wire &&) = delete;
|
|
||||||
Wire(const Wire &) = delete;
|
|
||||||
Wire &operator=(Wire &&) = delete;
|
|
||||||
Wire &operator=(const Wire &rhs) = delete;
|
|
||||||
|
|
||||||
template <WireFunction _Fn>
|
|
||||||
Wire(_Fn &&fn)
|
|
||||||
: _M_impl(_M_new_impl(std::forward <_Fn>(fn))), _M_cache(), _M_holds(), _M_dirty() {}
|
|
||||||
|
|
||||||
template <WireFunction _Fn>
|
|
||||||
Wire &operator=(_Fn &&fn) {
|
|
||||||
this->assign(std::forward <_Fn>(fn));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <WireFunction _Fn>
|
|
||||||
void assign(_Fn &&fn) {
|
|
||||||
this->_M_check_double_assign();
|
|
||||||
this->_M_impl.reset(this->_M_new_impl(std::forward <_Fn>(fn)));
|
|
||||||
this->_M_holds = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator max_size_t() const {
|
|
||||||
if (this->_M_holds == false) {
|
|
||||||
this->_M_cache = this->_M_impl->call();
|
|
||||||
this->_M_holds = true;
|
|
||||||
}
|
|
||||||
return this->_M_cache;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Register {
|
|
||||||
private:
|
|
||||||
friend struct Visitor;
|
|
||||||
|
|
||||||
max_size_t _M_new;
|
|
||||||
max_size_t _M_old;
|
|
||||||
|
|
||||||
[[no_unique_address]]
|
|
||||||
debug::DebugValue <bool, false> _M_dirty;
|
|
||||||
|
|
||||||
void sync() {
|
|
||||||
if (this->_M_dirty) {
|
|
||||||
this->_M_old = this->_M_new;
|
|
||||||
this->_M_dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_value(max_size_t value) {
|
|
||||||
this->_M_new = value;
|
|
||||||
debug::assert(!this->_M_dirty, "Register is already assigned in this cycle.");
|
|
||||||
this->_M_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto get_value() const -> max_size_t { return this->_M_old; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Register() : _M_new(), _M_old(), _M_dirty() {}
|
|
||||||
|
|
||||||
Register(Register &&) = delete;
|
|
||||||
Register(const Register &) = delete;
|
|
||||||
Register &operator=(Register &&) = delete;
|
|
||||||
Register &operator=(const Register &rhs) = delete;
|
|
||||||
|
|
||||||
template <std::convertible_to <max_size_t> _Int>
|
|
||||||
void operator <= (_Int &&value) {
|
|
||||||
this->set_value(static_cast <max_size_t>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
operator max_size_t() const { return this->get_value(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace dark::hardware
|
|
48
include/register.h
Normal file
48
include/register.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "debug.h"
|
||||||
|
#include "concept.h"
|
||||||
|
|
||||||
|
namespace dark {
|
||||||
|
|
||||||
|
template <std::size_t _Len>
|
||||||
|
struct Register {
|
||||||
|
private:
|
||||||
|
static_assert(0 < _Len && _Len <= kMaxLength,
|
||||||
|
"Register: _Len must be in range [1, kMaxLength].");
|
||||||
|
|
||||||
|
friend class Visitor;
|
||||||
|
|
||||||
|
max_size_t _M_old : _Len;
|
||||||
|
max_size_t _M_new : _Len;
|
||||||
|
|
||||||
|
[[no_unique_address]]
|
||||||
|
debug::DebugValue <bool, false> _M_assigned;
|
||||||
|
|
||||||
|
void sync() {
|
||||||
|
if (this->_M_assigned) {
|
||||||
|
this->_M_assigned = false;
|
||||||
|
this->_M_old = this->_M_new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool _Bit_Len = _Len;
|
||||||
|
|
||||||
|
explicit Register() : _M_old(), _M_new(), _M_assigned() {}
|
||||||
|
|
||||||
|
Register(Register &&) = delete;
|
||||||
|
Register(const Register &) = delete;
|
||||||
|
Register &operator=(Register &&) = delete;
|
||||||
|
Register &operator=(const Register &rhs) = delete;
|
||||||
|
|
||||||
|
template <concepts::bit_convertible <_Len> _Tp>
|
||||||
|
void operator <= (_Tp &&value) {
|
||||||
|
debug::assert(!this->_M_assigned, "Register is double assigned in this cycle.");
|
||||||
|
this->_M_assigned = true;
|
||||||
|
this->_M_new = static_cast <max_size_t> (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator max_size_t() const { return this->_M_old; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dark
|
@ -1,9 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "hardware.h"
|
|
||||||
#include "reflect.h"
|
#include "reflect.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
namespace dark::hardware {
|
namespace dark {
|
||||||
|
|
||||||
struct Visitor {
|
struct Visitor {
|
||||||
template <typename _Tp>
|
template <typename _Tp>
|
||||||
|
116
include/wire.h
Normal file
116
include/wire.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "debug.h"
|
||||||
|
#include "concept.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace dark {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template <typename _Fn, std::size_t _Len>
|
||||||
|
concept WireFunction = requires(_Fn &&func) {
|
||||||
|
{ func() } -> concepts::bit_convertible <_Len>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FuncBase {
|
||||||
|
using _Ret_t = int;
|
||||||
|
using _Cpy_t = FuncBase *;
|
||||||
|
virtual _Ret_t call() const = 0;
|
||||||
|
virtual _Cpy_t copy() const = 0;
|
||||||
|
virtual ~FuncBase() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t _Len, WireFunction <_Len> _Fn>
|
||||||
|
struct FuncImpl final : FuncBase {
|
||||||
|
_Fn _M_lambda;
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
FuncImpl(_Tp &&fn) : _M_lambda(std::forward <_Tp> (fn)) {}
|
||||||
|
|
||||||
|
_Ret_t call() const override { return this->_M_lambda(); }
|
||||||
|
_Cpy_t copy() const override { return new FuncImpl(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EmptyWire final : FuncBase {
|
||||||
|
_Ret_t call() const override {
|
||||||
|
debug::assert(false, "Empty wire is called.");
|
||||||
|
debug::unreachable();
|
||||||
|
}
|
||||||
|
_Cpy_t copy() const override { return new EmptyWire; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
|
||||||
|
template <std::size_t _Len>
|
||||||
|
struct Wire {
|
||||||
|
private:
|
||||||
|
static_assert(0 < _Len && _Len <= kMaxLength,
|
||||||
|
"Wire: _Len must be in range [1, kMaxLength].");
|
||||||
|
|
||||||
|
friend class Visitor;
|
||||||
|
|
||||||
|
using _Manage_t = std::unique_ptr <details::FuncBase>;
|
||||||
|
|
||||||
|
_Manage_t _M_func;
|
||||||
|
|
||||||
|
mutable max_size_t _M_cache;
|
||||||
|
mutable bool _M_holds;
|
||||||
|
|
||||||
|
[[no_unique_address]]
|
||||||
|
debug::DebugValue <bool, false> _M_assigned;
|
||||||
|
|
||||||
|
void sync() { this->_M_holds = false; }
|
||||||
|
|
||||||
|
template <details::WireFunction <_Len> _Fn>
|
||||||
|
static auto _M_new_func(_Fn &&fn) {
|
||||||
|
using _Decay_t = std::decay_t <_Fn>;
|
||||||
|
return new details::FuncImpl <_Len, _Decay_t> {std::forward <_Fn>(fn)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void _M_checked_assign() {
|
||||||
|
debug::assert(!this->_M_assigned, "Wire is assigned twice.");
|
||||||
|
this->_M_assigned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool _Bit_Len = _Len;
|
||||||
|
|
||||||
|
explicit Wire() :
|
||||||
|
_M_func(new details::EmptyWire),
|
||||||
|
_M_cache(), _M_holds(), _M_assigned() {}
|
||||||
|
|
||||||
|
explicit operator max_size_t() const {
|
||||||
|
if (this->_M_holds == false) {
|
||||||
|
this->_M_holds = true;
|
||||||
|
constexpr auto mask = make_mask <_Len> ();
|
||||||
|
this->_M_cache = this->_M_func->call() & mask;
|
||||||
|
}
|
||||||
|
return this->_M_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wire(Wire &&) = delete;
|
||||||
|
Wire(const Wire &) = delete;
|
||||||
|
Wire &operator=(Wire &&) = delete;
|
||||||
|
Wire &operator=(const Wire &rhs) = delete;
|
||||||
|
|
||||||
|
template <details::WireFunction <_Len> _Fn>
|
||||||
|
Wire(_Fn &&fn) :
|
||||||
|
_M_func(_M_new_func(std::forward <_Fn> (fn))),
|
||||||
|
_M_cache(), _M_holds(), _M_assigned() {}
|
||||||
|
|
||||||
|
template <details::WireFunction <_Len> _Fn>
|
||||||
|
Wire &operator=(_Fn &&fn) {
|
||||||
|
return this->assign(std::forward <_Fn> (fn)), *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <details::WireFunction <_Len> _Fn>
|
||||||
|
void assign(_Fn &&fn) {
|
||||||
|
this->_M_checked_assign();
|
||||||
|
this->_M_func.reset(_M_new_func(std::forward <_Fn> (fn)));
|
||||||
|
this->sync();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace dark
|
Reference in New Issue
Block a user