refactor: redesign the type system | embrace the new bit_type concept

This commit is contained in:
DarkSharpness
2024-07-11 20:38:05 +08:00
parent f3d2074a79
commit 1693df2820
10 changed files with 171 additions and 255 deletions

View File

@ -1,39 +0,0 @@
#include "../include/tools"
signed main() {
[[maybe_unused]]
Bit <1> a; // Create a 1-bit object, default to 0
Bit <10> b(10); // Create a 10-bit object
auto ref = b.slice <4, 2> (); // A copy of [2, 3, 4] bit
std::cout << b << std::endl; // 10
b.set <3, 1> (ref); // Set [1, 2, 3] bit to [2, 3, 4] bit
std::cout << b << std::endl; // 4
b.set <0> (~a); // Set the 0-th bit to ~a (= 1 in this case)
std::cout << b << std::endl; // 5
auto sec = b.slice <2> (); // A copy of the 2-th bit (= 1 in this case)
auto d = sec.zero_extend(); // Zero extend (default to 32-bit)
std::cout << d << std::endl; // 1
auto c = sec.sign_extend <3> (); // Sign extend (default to 32-bit)
std::cout << c << std::endl; // 7
// c + d; // Error: different size
// a += 1; // Error: no assignment-operation operator
auto e = b - 1; // normal integer can be assumed as any size
std::cout << e << std::endl; // 4
return 0;
}

View File

@ -1,38 +0,0 @@
#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;
}

View File

@ -1,20 +1,13 @@
#pragma once #pragma once
#include "target.h" #include "concept.h"
#include "debug.h" #include "debug.h"
#include <version> #include <version>
#include <climits>
#include <concepts> #include <concepts>
namespace dark::bits { namespace dark {
static constexpr std::size_t kMaxLength = 8 * sizeof(target_size_t); static constexpr std::size_t kMaxLength = std::numeric_limits<max_size_t>::digits;
template <int _First>
constexpr auto int_concat(target_size_t arg) { return arg; }
template <int _First, int ..._Lens>
constexpr auto int_concat(target_size_t arg, auto ...args) {
return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...);
}
template <std::size_t _Nm> template <std::size_t _Nm>
struct Bit { struct Bit {
@ -22,117 +15,38 @@ struct Bit {
static_assert(0 < _Nm && _Nm <= kMaxLength, static_assert(0 < _Nm && _Nm <= kMaxLength,
"Bit: _Nm out of range. Should be in [1, kMaxLength]"); "Bit: _Nm out of range. Should be in [1, kMaxLength]");
target_size_t _M_data : _Nm; // Real storage max_size_t _M_data : _Nm; // Real storage
template <std::size_t _Hi, std::size_t _Lo>
static constexpr void _M_range_check();
public: public:
static constexpr std::size_t _Bit_Len = _Nm; static constexpr std::size_t _Bit_Len = _Nm;
constexpr Bit(target_size_t data = {}) : _M_data(data) {} constexpr Bit(max_size_t data = 0) : _M_data(data) {}
template <std::size_t ..._Lens> constexpr explicit operator max_size_t() const { return this->_M_data; }
constexpr Bit(Bit<_Lens> ...args) requires ((_Lens + ...) == _Nm) :
_M_data(int_concat<_Lens...>(args...)) {}
constexpr Bit(const Bit &val) = default; template <concepts::bit_type ..._Tp>
requires ((_Tp::_Bit_Len + ...) == _Nm)
constexpr Bit(const _Tp &...args);
template <std::size_t _Len> template <concepts::bit_match <Bit> _Tp>
constexpr Bit(const Bit <_Len> &val) requires (_Len != _Nm) { constexpr Bit &operator=(const _Tp &val);
static_assert(_Len == _Nm, "Bit: bit length mismatch");
}
constexpr Bit &operator=(const Bit &val) = default; template <std::size_t _Hi, std::size_t _Lo = _Hi, concepts::bit_match <Bit> _Tp>
constexpr void set(const _Tp &val);
template <std::size_t _Len> requires (_Len != _Nm)
constexpr Bit &operator=(const Bit <_Len> &val) {
static_assert(_Len == _Nm, "Bit: bit length mismatch");
return *this;
}
constexpr Bit &operator=(target_size_t val) {
this->_M_data = val;
return *this;
}
constexpr operator target_size_t() const { return this->_M_data; }
template <std::size_t _Hi, std::size_t _Lo = _Hi> template <std::size_t _Hi, std::size_t _Lo = _Hi>
constexpr auto set(Bit <_Hi - _Lo + 1> val) { constexpr auto range() const -> Bit <_Hi - _Lo + 1>;
static_cast <void> (this->slice<_Hi, _Lo>());
const auto mask = // Mask those bit in the middle
(target_size_t(1) << (_Hi + 1)) - (target_size_t(1) << _Lo);
const auto data = // Set those bit in the middle
static_cast <target_size_t> (val) << _Lo;
this->_M_data = (this->_M_data & ~mask) | data;
}
template <std::size_t _Hi, std::size_t _Lo = _Hi>
constexpr auto slice() const -> Bit <_Hi - _Lo + 1> {
static_assert(_Lo <= _Hi, "Bit::slice: _Lo should be no greater than _Hi");
static_assert(_Hi < _Nm, "Bit::slice: _Hi should be less than _Nm");
return Bit<_Hi - _Lo + 1>(this->_M_data >> _Lo);
}
template <std::size_t _Len = 1> template <std::size_t _Len = 1>
constexpr auto at(target_size_t pos) const -> Bit <_Len> { constexpr auto slice(std::size_t pos) const -> Bit <_Len>;
static_assert(_Len != 0, "Bit::at: _Len should be greater than 0");
debug::assert(pos + _Len <= _Nm, "Bit::at: pos out of range");
return Bit <_Len> (this->_M_data >> pos);
}
constexpr auto operator [](target_size_t pos) const -> Bit <1> { return this->at(pos); } constexpr Bit <1> operator [](std::size_t pos) const { return this->slice(pos); }
template <std::size_t _New = kMaxLength>
constexpr auto zero_extend() const -> Bit<_New>;
template <std::size_t _New = kMaxLength>
constexpr auto sign_extend() const -> Bit<_New>;
}; };
template <typename _Tp> template <concepts::bit_type ..._Tp>
static constexpr bool is_bit_v = false;
template <std::size_t _Nm>
static constexpr bool is_bit_v<Bit<_Nm>> = true;
template <typename _Tp>
concept BitType = is_bit_v<_Tp>;
template <BitType ..._Tp>
Bit(_Tp...) -> Bit<(_Tp::_Bit_Len + ...)>; Bit(_Tp...) -> Bit<(_Tp::_Bit_Len + ...)>;
template <std::size_t _Old, std::size_t _New = kMaxLength> } // namespace dark
constexpr auto sign_extend(target_size_t val) {
static_assert(_Old < _New, "sign_extend: _Old should be less than _New");
struct { target_ssize_t _M_data : _Old; } tmp;
return Bit<_New>(tmp._M_data = val);
}
template <std::size_t _New = kMaxLength, BitType _Tp>
constexpr auto sign_extend(_Tp val) {
return sign_extend<_Tp::_Bit_Len, _New>(static_cast<target_size_t>(val));
}
template <std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto zero_extend(target_size_t val) {
static_assert(_Old < _New, "zero_extend: _Old should be less than _New");
struct { target_size_t _M_data : _Old; } tmp;
return Bit<_New>(tmp._M_data = val);
}
template <std::size_t _New = kMaxLength, BitType _Tp>
constexpr auto zero_extend(_Tp val) {
return zero_extend<_Tp::_Bit_Len, _New>(static_cast<target_size_t>(val));
}
template <std::size_t _Nm>
template <std::size_t _New>
constexpr auto Bit<_Nm>::sign_extend() const -> Bit<_New> {
return ::dark::bits::sign_extend<_New> (*this);
}
template <std::size_t _Nm>
template <std::size_t _New>
constexpr auto Bit<_Nm>::zero_extend() const -> Bit<_New> {
return ::dark::bits::zero_extend<_New> (*this);
}
} // namespace dark::bits

94
include/bit_impl.h Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#include "bit.h"
namespace dark {
template <int _First>
constexpr auto int_concat(max_size_t arg) { return arg; }
template <int _First, int ..._Lens>
constexpr auto int_concat(max_size_t arg, auto ...args) {
return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...);
}
template <std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto sign_extend(max_size_t val) {
static_assert(_Old < _New, "sign_extend: _Old should be less than _New");
struct { max_ssize_t _M_data : _Old; } tmp;
return Bit<_New>(tmp._M_data = val);
}
template <std::size_t _New = kMaxLength, concepts::bit_type _Tp>
constexpr auto sign_extend(const _Tp & val) {
return sign_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val));
}
template <std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto zero_extend(max_size_t val) {
static_assert(_Old < _New, "zero_extend: _Old should be less than _New");
struct { max_size_t _M_data : _Old; } tmp;
return Bit<_New>(tmp._M_data = val);
}
template <std::size_t _New = kMaxLength, concepts::bit_type _Tp>
constexpr auto zero_extend(const _Tp &val) {
return zero_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val));
}
template <std::size_t _Nm>
template <concepts::bit_type ..._Tp>
requires ((_Tp::_Bit_Len + ...) == _Nm)
constexpr Bit<_Nm>::Bit(const _Tp &...args)
: _M_data(int_concat<_Tp::_Bit_Len...>(static_cast<max_size_t>(args)...)) {}
template <std::size_t _Nm>
template <concepts::bit_match <Bit <_Nm>> _Tp>
constexpr Bit<_Nm> &Bit<_Nm>::operator=(const _Tp &val) {
this->_M_data = static_cast<max_size_t>(val);
return *this;
}
template <std::size_t _Nm>
template <std::size_t _Hi, std::size_t _Lo>
constexpr void Bit<_Nm>::_M_range_check() {
static_assert(_Lo <= _Hi, "Bit::range_check: _Lo should be no greater than _Hi");
static_assert(_Hi < _Nm, "Bit::range_check: _Hi should be less than _Nm");
}
template <std::size_t _Nm>
template <std::size_t _Hi, std::size_t _Lo, concepts::bit_match <Bit<_Nm>> _Tp>
constexpr void Bit<_Nm>::set(const _Tp &val) {
this->_M_range_check<_Hi, _Lo>();
auto data = static_cast<max_size_t>(val);
constexpr auto _Length = _Hi - _Lo + 1;
if constexpr (_Length == kMaxLength) {
this->_M_data = data;
} else {
auto mask = ((1 << _Length) - 1) << _Lo;
this->_M_data = (this->_M_data & ~mask) | ((data << _Lo) & mask);
}
}
template <std::size_t _Nm>
template <std::size_t _Hi, std::size_t _Lo>
constexpr auto Bit<_Nm>::range() const -> Bit <_Hi - _Lo + 1> {
this->_M_range_check<_Hi, _Lo>();
constexpr auto _Length = _Hi - _Lo + 1;
return Bit<_Length>(this->_M_data >> _Lo);
}
template <std::size_t _Nm>
template <std::size_t _Len>
constexpr auto Bit<_Nm>::slice(std::size_t pos) const -> Bit <_Len> {
static_assert(_Len <= _Nm, "Bit::slice: _Len should be no greater than _Nm");
debug::assert(pos <= _Nm - _Len, "Bit::slice: pos should be less than _Nm - _Len");
if constexpr (_Len == kMaxLength) {
return *this;
} else {
return Bit<_Len>(this->_M_data >> pos);
}
}
} // namespace dark

1
include/bit_op.h Normal file
View File

@ -0,0 +1 @@
#pragma once

View File

@ -1,32 +0,0 @@
#pragma once
#include "bit.h"
namespace dark::bits {
template <typename _Tp>
concept NonBit = !BitType<_Tp>;
template <typename _Tp>
auto cast(_Tp &&val) -> target_size_t {
return static_cast<target_size_t>(val);
}
template <BitType _Lhs, BitType _Rhs>
constexpr auto operator + (_Lhs lhs, _Rhs rhs) {
static_assert(_Lhs::_Bit_Len == _Rhs::_Bit_Len,
"operator +: lhs and rhs should have the same length");
return _Lhs { cast(lhs) + cast(rhs) };
}
template <BitType _Lhs, NonBit _Rhs>
constexpr auto operator + (_Lhs lhs, _Rhs &&rhs) {
return _Lhs { cast(lhs) + cast(rhs) };
}
template <NonBit _Lhs, BitType _Rhs>
constexpr auto operator + (_Lhs &&lhs, _Rhs rhs) {
return _Rhs { cast(lhs) + cast(rhs) };
}
} // namespace dark::bits

41
include/concept.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <cstdint>
#include <concepts>
namespace dark {
using max_size_t = std::uint32_t;
using max_ssize_t = std::int32_t;
} // namespace dark
namespace dark::concepts {
template <typename _Tp>
using func_t = void(*)(_Tp);
template <typename _From, typename _To>
concept implicit_convertible_to = requires(_From &a, func_t <_To> b) {
b(a); // Can implicitly convert
};
template <typename _From, typename _To>
concept explicit_convertible_to =
!implicit_convertible_to <_From, _To>
&& std::constructible_from <_To, _From>;
template <typename _Tp>
concept has_length = requires { _Tp::_Bit_Len; };
template <typename _Tp>
concept bit_type = has_length <_Tp> && explicit_convertible_to <_Tp, max_size_t>;
template <typename _Tp>
concept int_type = !has_length <_Tp> && implicit_convertible_to <_Tp, max_size_t>;
template <typename _Lhs, typename _Rhs>
concept bit_match =
(bit_type <_Lhs> && bit_type <_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len)
|| (int_type <_Lhs> || int_type <_Rhs>);
} // namespace dark::concepts

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "debug.h" #include "debug.h"
#include "target.h" #include "concept.h"
#include <memory> #include <memory>
#include <cstdint>
#include <concepts> #include <concepts>
namespace dark::hardware { namespace dark::hardware {
@ -13,7 +14,7 @@ struct Register;
template <typename _Fn> template <typename _Fn>
concept WireFunction = concept WireFunction =
!std::same_as <Wire, std::decay_t <_Fn>> && !std::same_as <Wire, std::decay_t <_Fn>> &&
requires(_Fn &&__f) { { __f() } -> std::convertible_to <target_size_t>; }; requires(_Fn &&__f) { { __f() } -> std::convertible_to <max_size_t>; };
struct WireBase { struct WireBase {
using _Ret_t = int; using _Ret_t = int;
@ -46,7 +47,7 @@ struct Wire {
using _Manage_t = WireBase; using _Manage_t = WireBase;
std::unique_ptr <_Manage_t> _M_impl; std::unique_ptr <_Manage_t> _M_impl;
mutable target_size_t _M_cache; mutable max_size_t _M_cache;
mutable bool _M_holds; mutable bool _M_holds;
[[no_unique_address]] [[no_unique_address]]
@ -65,7 +66,6 @@ struct Wire {
} }
public: public:
Wire() : _M_impl(new EmptyWire), _M_cache(), _M_holds(), _M_dirty() {} Wire() : _M_impl(new EmptyWire), _M_cache(), _M_holds(), _M_dirty() {}
Wire(Wire &&) = delete; Wire(Wire &&) = delete;
@ -90,7 +90,7 @@ struct Wire {
this->_M_holds = false; this->_M_holds = false;
} }
operator target_size_t() const { operator max_size_t() const {
if (this->_M_holds == false) { if (this->_M_holds == false) {
this->_M_cache = this->_M_impl->call(); this->_M_cache = this->_M_impl->call();
this->_M_holds = true; this->_M_holds = true;
@ -103,8 +103,8 @@ struct Register {
private: private:
friend struct Visitor; friend struct Visitor;
target_size_t _M_new; max_size_t _M_new;
target_size_t _M_old; max_size_t _M_old;
[[no_unique_address]] [[no_unique_address]]
debug::DebugValue <bool, false> _M_dirty; debug::DebugValue <bool, false> _M_dirty;
@ -116,15 +116,16 @@ struct Register {
} }
} }
void set_value(target_size_t value) { void set_value(max_size_t value) {
this->_M_new = value; this->_M_new = value;
debug::assert(!this->_M_dirty, "Register is already assigned in this cycle."); debug::assert(!this->_M_dirty, "Register is already assigned in this cycle.");
this->_M_dirty = true; this->_M_dirty = true;
} }
auto get_value() const -> target_size_t { return this->_M_old; } auto get_value() const -> max_size_t { return this->_M_old; }
public: public:
Register() : _M_new(), _M_old(), _M_dirty() {} Register() : _M_new(), _M_old(), _M_dirty() {}
Register(Register &&) = delete; Register(Register &&) = delete;
@ -132,12 +133,12 @@ struct Register {
Register &operator=(Register &&) = delete; Register &operator=(Register &&) = delete;
Register &operator=(const Register &rhs) = delete; Register &operator=(const Register &rhs) = delete;
template <std::convertible_to <target_size_t> _Int> template <std::convertible_to <max_size_t> _Int>
void operator <= (_Int &&value) { void operator <= (_Int &&value) {
this->set_value(static_cast <target_size_t>(value)); this->set_value(static_cast <max_size_t>(value));
} }
operator target_size_t() const { return this->get_value(); } operator max_size_t() const { return this->get_value(); }
}; };
} // namespace dark::hardware } // namespace dark::hardware

View File

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

View File

@ -1,17 +0,0 @@
#pragma once
#include "target.h"
#include "hardware.h"
#include "synchronize.h"
#include "bit.h"
#include "bitop.h"
using ::dark::target_size_t;
using ::dark::target_ssize_t;
using ::dark::hardware::Register;
using ::dark::hardware::Wire;
using ::dark::hardware::Visitor;
using ::dark::hardware::SyncTags;
using ::dark::hardware::sync_member;
using ::dark::bits::Bit;