#pragma once #include "debug.h" #include "target.h" #include #include namespace dark::hardware { struct Visitor; struct Wire; struct Register; template concept WireFunction = !std::same_as > && requires(_Fn &&__f) { { __f() } -> std::convertible_to ; }; struct WireBase { using _Ret_t = int; using _Cpy_t = WireBase *; virtual _Ret_t call() const = 0; virtual ~WireBase() = default; }; template struct WireImpl final : WireBase { _Fn _M_lambda; template 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 target_size_t _M_cache; mutable bool _M_holds; [[no_unique_address]] debug::DebugValue _M_dirty; // Can be assigned only once void sync() { this->_M_holds = false; } template static auto _M_new_impl(_Fn &&fn) -> _Manage_t * { return new WireImpl > {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 Wire(_Fn &&fn) : _M_impl(_M_new_impl(std::forward <_Fn>(fn))), _M_cache(), _M_holds(), _M_dirty() {} template Wire &operator=(_Fn &&fn) { this->assign(std::forward <_Fn>(fn)); return *this; } template 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 target_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; target_size_t _M_new; target_size_t _M_old; [[no_unique_address]] debug::DebugValue _M_dirty; void sync() { if (this->_M_dirty) { this->_M_old = this->_M_new; this->_M_dirty = false; } } void set_value(target_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 -> target_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 _Int> void operator <= (_Int &&value) { this->set_value(static_cast (value)); } operator target_size_t() const { return this->get_value(); } }; } // namespace dark::hardware