commit 68dd99a8268c155012f331d2d3c38537bd82fb6a Author: DarkSharpness <2040703891@qq.com> Date: Tue Jul 9 21:04:36 2024 +0800 initial commit | complete a draft outline diff --git a/demo/demo.cpp b/demo/demo.cpp new file mode 100644 index 0000000..9060079 --- /dev/null +++ b/demo/demo.cpp @@ -0,0 +1,40 @@ +#include "../include/tools" + +struct Input { + Wire a; + Wire b; + std::array c; +}; + +struct Output { + Register d; + Register e; +}; + +struct Content { + Register r; +}; + +struct MyModule : Input, Output, private Content { + using Tags = SyncTags ; + friend class Visitor; + + void demo() { + this->d <= this->a + this->b; + } +}; + +signed main() { + MyModule m; + + m.a = []() { return 1; }; + m.b.assign([&]() { return (target_size_t)m.d; }); + + for (int i = 0 ; i < 10 ; ++i) { + std::cout << m.d << std::endl; + m.demo(); + std::cout << m.d << std::endl; + sync_member(m); + } + return 0; +} diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..601d3f4 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include + +namespace dark::debug { + +/** + * Copied from https://en.cppreference.com/w/cpp/utility/unreachable + * If compiled under C++23, just use std::unreachable() instead. +*/ +[[noreturn]] inline void unreachable() { +#if __cplusplus > 202002L + std::unreachable(); +#elif defined(_MSC_VER) && !defined(__clang__) // MSVC + // Uses compiler specific extensions if possible. + // Even if no extension is used, undefined behavior is still raised by + // an empty function body and the noreturn attribute. + __assume(false); +#else // GCC, Clang + __builtin_unreachable(); +#endif +} + +template +struct assert { + explicit assert(_Tp &&condition, _Args &&...args, + std::source_location location = std::source_location::current()) { + if (condition) return; + std::cerr << "Assertion failed at: " + << location.file_name() << ":" << location.line() << "\n"; + if constexpr (sizeof...(args) != 0) { + std::cerr << "Message: "; + ((std::cerr << args), ...) << std::endl; + } + std::exit(EXIT_FAILURE); + } +}; + +template +assert(_Tp &&, _Args &&...) -> assert<_Tp, _Args...>; + +template +struct DebugValue { +#ifdef _DEBUG + private: + _Tp _M_value = _Default; + public: + auto get_value() const { return this->_M_value; } + auto set_value(_Tp value) { this->_M_value = value; } +#else + public: + auto get_value() const { return _Default; } + auto set_value(_Tp) { /* do nothing */ } +#endif + public: + explicit operator _Tp() const { return this->get_value(); } + DebugValue &operator=(_Tp value) { this->set_value(value); return *this; } +}; + +} // namespace dark::debug diff --git a/include/hardware.h b/include/hardware.h new file mode 100644 index 0000000..6f659ec --- /dev/null +++ b/include/hardware.h @@ -0,0 +1,138 @@ +#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() {} + + template _Int> + void operator <= (_Int &&value) { + this->set_value(static_cast (value)); + } + + operator target_size_t() const { return this->get_value(); } +}; + +} // namespace dark::hardware diff --git a/include/reflect.h b/include/reflect.h new file mode 100644 index 0000000..d3a1dfd --- /dev/null +++ b/include/reflect.h @@ -0,0 +1,81 @@ +#pragma once +#include +#include + +namespace dark::reflect { + +/* A init helper to get the size of a struct. */ +struct init_helper { template operator _Tp(); }; + +/* A size helper to get the size of a struct. */ +template requires std::is_aggregate_v <_Tp> +inline consteval auto member_size_aux(auto &&...args) -> std::size_t { + constexpr std::size_t size = sizeof...(args); + constexpr std::size_t maximum = 114; + if constexpr (size > maximum) { + static_assert (sizeof(_Tp) == 0, "The struct has too many members."); + } else if constexpr (!requires {_Tp { args... }; }) { + return size - 1; + } else { + return member_size_aux <_Tp> (args..., init_helper {}); + } +} + +/* Return the member size for a aggregate type without base. */ +template requires std::is_aggregate_v <_Tp> +inline consteval auto member_size(_Tp &) -> std::size_t { return member_size_aux <_Tp> (); } + +template requires std::is_aggregate_v <_Tp> +inline consteval auto member_size() -> std::size_t { return member_size_aux <_Tp> (); } + +template requires std::is_aggregate_v <_Tp> +auto tuplify(_Tp &value) { + constexpr auto size = member_size <_Tp> (); + if constexpr (size == 1) { + auto &[x0] = value; + return std::forward_as_tuple(x0); + } else if constexpr (size == 2) { + auto &[x0, x1] = value; + return std::forward_as_tuple(x0, x1); + } else if constexpr (size == 3) { + auto &[x0, x1, x2] = value; + return std::forward_as_tuple(x0, x1, x2); + } else if constexpr (size == 4) { + auto &[x0, x1, x2, x3] = value; + return std::forward_as_tuple(x0, x1, x2, x3); + } else if constexpr (size == 5) { + auto &[x0, x1, x2, x3, x4] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4); + } else if constexpr (size == 6) { + auto &[x0, x1, x2, x3, x4, x5] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5); + } else if constexpr (size == 7) { + auto &[x0, x1, x2, x3, x4, x5, x6] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6); + } else if constexpr (size == 8) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7); + } else if constexpr (size == 9) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8); + } else if constexpr (size == 10) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9); + } else if constexpr (size == 11) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10); + } else if constexpr (size == 12) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11); + } else if constexpr (size == 13) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12); + } else if constexpr (size == 14) { + auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13] = value; + return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13); + } else { + static_assert (sizeof(_Tp) == 0, "The struct has too many members."); + } +} + +} // namespace dark::reflect diff --git a/include/synchronize.h b/include/synchronize.h new file mode 100644 index 0000000..eed40d0 --- /dev/null +++ b/include/synchronize.h @@ -0,0 +1,59 @@ +#pragma once +#include "hardware.h" +#include "reflect.h" +#include + +namespace dark::hardware { + +struct Visitor { + template + static constexpr bool is_syncable_v = + requires(_Tp &val) { { val.sync() } -> std::same_as; }; + + template requires is_syncable_v<_Tp> + static void sync(_Tp &val) { val.sync(); } + + template + static _Base &cast(_Tp &value) { return static_cast<_Base &>(value); } +}; + +template +struct SyncTags {}; + +template +static constexpr bool is_valid_tag_v = false; +template +static constexpr bool is_valid_tag_v> = true; +template +concept has_valid_tag = is_valid_tag_v; + +template +static constexpr bool is_std_array_v = std::is_array_v<_Tp>; +template +static constexpr bool is_std_array_v> = true; + +template +inline void sync_member(_Tp &value); + +template +inline void sync_by_tag(_Tp &value, SyncTags<_Base...>) { + (sync_member(Visitor::cast<_Tp, _Base>(value)), ...); +} + +template +inline void sync_member(_Tp &value) { + if constexpr (is_std_array_v<_Tp>) { + for (auto &member : value) sync_member(member); + } else if constexpr (Visitor::is_syncable_v<_Tp>) { + Visitor::sync(value); + } else if constexpr (has_valid_tag<_Tp>) { + sync_by_tag(value, typename _Tp::Tags {}); + } else if constexpr (std::is_aggregate_v<_Tp>) { + auto &&tuple = reflect::tuplify(value); + std::apply([](auto &...members) { (sync_member(members), ...); }, tuple); + } else { + static_assert(sizeof(_Tp) == 0, "This type is not syncable."); + } +} + +} // namespace dark::hardware diff --git a/include/target.h b/include/target.h new file mode 100644 index 0000000..8daf92c --- /dev/null +++ b/include/target.h @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace dark { + +using target_size_t = std::uint32_t; +using target_ssize_t = std::int32_t; + +} // namespace dark diff --git a/include/template.h b/include/template.h new file mode 100644 index 0000000..e69de29