initial commit | complete a draft outline

This commit is contained in:
DarkSharpness
2024-07-09 21:04:36 +08:00
commit 68dd99a826
7 changed files with 388 additions and 0 deletions

40
demo/demo.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "../include/tools"
struct Input {
Wire a;
Wire b;
std::array <Wire, 2> c;
};
struct Output {
Register d;
Register e;
};
struct Content {
Register r;
};
struct MyModule : Input, Output, private Content {
using Tags = SyncTags <Input, Output, Content>;
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;
}

61
include/debug.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include <utility>
#include <iostream>
#include <source_location>
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 <typename _Tp, typename... _Args>
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 <typename _Tp, typename... _Args>
assert(_Tp &&, _Args &&...) -> assert<_Tp, _Args...>;
template <typename _Tp, _Tp _Default>
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

138
include/hardware.h Normal file
View File

@ -0,0 +1,138 @@
#pragma once
#include "debug.h"
#include "target.h"
#include <memory>
#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 <target_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 target_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 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 <bool, false> _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 <std::convertible_to <target_size_t> _Int>
void operator <= (_Int &&value) {
this->set_value(static_cast <target_size_t>(value));
}
operator target_size_t() const { return this->get_value(); }
};
} // namespace dark::hardware

81
include/reflect.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <tuple>
#include <concepts>
namespace dark::reflect {
/* A init helper to get the size of a struct. */
struct init_helper { template <typename _Tp> operator _Tp(); };
/* A size helper to get the size of a struct. */
template <typename _Tp> 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 <typename _Tp> requires std::is_aggregate_v <_Tp>
inline consteval auto member_size(_Tp &) -> std::size_t { return member_size_aux <_Tp> (); }
template <typename _Tp> requires std::is_aggregate_v <_Tp>
inline consteval auto member_size() -> std::size_t { return member_size_aux <_Tp> (); }
template <typename _Tp> 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

59
include/synchronize.h Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include "hardware.h"
#include "reflect.h"
#include <array>
namespace dark::hardware {
struct Visitor {
template <typename _Tp>
static constexpr bool is_syncable_v =
requires(_Tp &val) { { val.sync() } -> std::same_as<void>; };
template <typename _Tp> requires is_syncable_v<_Tp>
static void sync(_Tp &val) { val.sync(); }
template <typename _Tp, typename _Base>
static _Base &cast(_Tp &value) { return static_cast<_Base &>(value); }
};
template <typename ..._Base>
struct SyncTags {};
template <typename _Tp>
static constexpr bool is_valid_tag_v = false;
template <typename ..._Base>
static constexpr bool is_valid_tag_v<SyncTags<_Base...>> = true;
template <typename _Tp>
concept has_valid_tag = is_valid_tag_v<typename _Tp::Tags>;
template <typename _Tp>
static constexpr bool is_std_array_v = std::is_array_v<_Tp>;
template <typename _Tp, std::size_t _Nm>
static constexpr bool is_std_array_v<std::array<_Tp, _Nm>> = true;
template <typename _Tp>
inline void sync_member(_Tp &value);
template <typename _Tp, typename ..._Base>
inline void sync_by_tag(_Tp &value, SyncTags<_Base...>) {
(sync_member(Visitor::cast<_Tp, _Base>(value)), ...);
}
template <typename _Tp>
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

9
include/target.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <cstdint>
namespace dark {
using target_size_t = std::uint32_t;
using target_ssize_t = std::int32_t;
} // namespace dark

0
include/template.h Normal file
View File