diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..475dbc7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.20) +project(simulator) + +file(GLOB_RECURSE sources "src/*.cpp") + +set(CMAKE_CXX_STANDARD 20) + +include_directories(include) + +add_executable(simulator ${sources}) + +add_executable(alu demo/alu.cpp) diff --git a/demo/alu.cpp b/demo/alu.cpp index 08c4f00..563962d 100644 --- a/demo/alu.cpp +++ b/demo/alu.cpp @@ -1,131 +1,111 @@ -#include "../include/tools.h" +#include "tools.h" #include - -bool reset; -bool ready; +#include // RISC-V enum class Opcode : dark::max_size_t { - ADD, - SUB, - SLL, - SRL, - SRA, - AND, - OR, - XOR, - SLT, - SLTU, - SGE, - SGEU, - SEQ, - SNEQ + ADD, + SUB, + SLL, + SRL, + SRA, + AND, + OR, + XOR, + SLT, + SLTU, + SGE, + SGEU, + SEQ, + SNEQ }; - // Normally, only wire can be used in the input. struct AluInput { - Wire <8> opcode; - Wire <1> issue; - Wire <32> rs1; - Wire <32> rs2; + Wire<8> opcode; + Wire<1> issue; + Wire<32> rs1; + Wire<32> rs2; }; struct AluOutput { - Register <32> out; - Register <1> done; + Register<32> out; + Register<1> done; }; -struct AluModule : AluInput, AluOutput { - using Tags = SyncTags; - - void work() { - if (reset) { - done <= 0; - } else if(ready && issue) { - switch (static_cast (static_cast (opcode))) { - using enum Opcode; - case ADD: out <= (rs1 + rs2); break; - case SUB: out <= (rs1 - rs2); break; - case SLL: out <= (rs1 << rs2); break; - case SRL: out <= (rs1 >> rs2); break; - case SRA: out <= (to_signed(rs1) >> to_unsigned(rs2)); - case AND: out <= (rs1 & rs2); break; - case OR: out <= (rs1 | rs2); break; - case XOR: out <= (rs1 ^ rs2); break; - case SLT: out <= (to_signed(rs1) < to_signed(rs2)); break; - case SLTU: out <= (rs1 < rs2); break; - case SGE: out <= (to_signed(rs1) >= to_signed(rs2)); break; - case SGEU: out <= (rs1 >= rs2); break; - case SEQ: out <= (rs1 == rs2); break; - case SNEQ: out <= (rs1 != rs2); break; - default: dark::debug::assert(false, "Invalid opcode"); - } - done <= 1; - } else { - done <= 0; - } - } - +struct AluModule : dark::Module { + void work() override { + if (issue) { + switch (static_cast(static_cast(opcode))) { + using enum Opcode; + case ADD: out <= (rs1 + rs2); break; + case SUB: out <= (rs1 - rs2); break; + case SLL: out <= (rs1 << rs2); break; + case SRL: out <= (rs1 >> rs2); break; + case SRA: out <= (to_signed(rs1) >> to_unsigned(rs2)); + case AND: out <= (rs1 & rs2); break; + case OR: out <= (rs1 | rs2); break; + case XOR: out <= (rs1 ^ rs2); break; + case SLT: out <= (to_signed(rs1) < to_signed(rs2)); break; + case SLTU: out <= (rs1 < rs2); break; + case SGE: out <= (to_signed(rs1) >= to_signed(rs2)); break; + case SGEU: out <= (rs1 >= rs2); break; + case SEQ: out <= (rs1 == rs2); break; + case SNEQ: out <= (rs1 != rs2); break; + default: dark::debug::assert(false, "Invalid opcode"); + } + done <= 1; + } + else { + done <= 0; + } + } }; -signed main() { - AluModule alu; +int main() { + std::string opstring; - std::string opstring; + max_size_t opcode; + max_size_t issue; + max_size_t rs1; + max_size_t rs2; - max_size_t opcode; - max_size_t issue; - max_size_t rs1; - max_size_t rs2; - - ready = 1; - - alu.opcode = [&]() { return opcode; }; - alu.issue = [&]() { return issue; }; - alu.rs1 = [&]() { return rs1; }; - alu.rs2 = [&]() { return rs2; }; - - while (std::cin >> opstring) { - issue = 1; - std::cin >> rs1 >> rs2; - if (opstring == "add") { - opcode = static_cast (Opcode::ADD); - } else if (opstring == "sub") { - opcode = static_cast (Opcode::SUB); - } else if (opstring == "sll") { - opcode = static_cast (Opcode::SLL); - } else if (opstring == "srl") { - opcode = static_cast (Opcode::SRL); - } else if (opstring == "sra") { - opcode = static_cast (Opcode::SRA); - } else if (opstring == "and") { - opcode = static_cast (Opcode::AND); - } else if (opstring == "or") { - opcode = static_cast (Opcode::OR); - } else if (opstring == "xor") { - opcode = static_cast (Opcode::XOR); - } else if (opstring == "slt") { - opcode = static_cast (Opcode::SLT); - } else if (opstring == "sltu") { - opcode = static_cast (Opcode::SLTU); - } else if (opstring == "sge") { - opcode = static_cast (Opcode::SGE); - } else if (opstring == "sgeu") { - opcode = static_cast (Opcode::SGEU); - } else if (opstring == "seq") { - opcode = static_cast (Opcode::SEQ); - } else if (opstring == "sneq") { - opcode = static_cast (Opcode::SNEQ); - } else { - std::cout << "Invalid opcode" << std::endl; - issue = 0; - } - - alu.work(); - - std::cout << to_unsigned(alu.out) << std::endl; - sync_member(alu); - } + dark::CPU cpu; + AluModule alu; + alu.opcode = [&]() { return opcode; }; + alu.issue = [&]() { return issue; }; + alu.rs1 = [&]() { return rs1; }; + alu.rs2 = [&]() { return rs2; }; + cpu.add_module(&alu); + std::unordered_map cmd2op = { + {"add", Opcode::ADD}, + {"sub", Opcode::SUB}, + {"sll", Opcode::SLL}, + {"src", Opcode::SRL}, + {"sra", Opcode::SRA}, + {"and", Opcode::AND}, + {"or", Opcode::OR}, + {"xor", Opcode::XOR}, + {"slt", Opcode::SLT}, + {"sltu", Opcode::SLTU}, + {"sge", Opcode::SGE}, + {"sgeu", Opcode::SGEU}, + {"seq", Opcode::SEQ}, + {"sneq", Opcode::SNEQ}}; + while (std::cin >> opstring) { + if (cmd2op.find(opstring) == cmd2op.end()) { + std::cout << "Invalid opcode" << std::endl; + issue = 0; + } + else { + issue = 1; + std::cin >> rs1 >> rs2; + } + opcode = static_cast(cmd2op[opstring]); + cpu.run_once(); + std::cout << "out: " << static_cast(alu.out) << std::endl; + std::cout << "done: " << static_cast(alu.done) << std::endl; + } + return 0; } \ No newline at end of file diff --git a/docs/frame.md b/docs/frame.md new file mode 100644 index 0000000..ccf6031 --- /dev/null +++ b/docs/frame.md @@ -0,0 +1,63 @@ +# 整体框架 + +cpu应当区分为很多个模块,每个模块应当交由 `CPU` 类进行管理。 +在配置好模块后,通过 `add_module` 函数将模块添加到 `CPU` 类中。 +这里,可以选择将 unique_ptr 传入,也可以选择将裸指针传入;对于后者,你应该自行确保内存访问安全。 + +在定义每个模块时,我们认为,模块中需要存储的值由三部分构成:Input, Output 和内部隐藏的数据。 + +因此,对于每个模块,你需要定义两个或三个**简单结构体**以表示这些数据,然后通过多继承的方法来定义模块。 +为了便于进行每个周期的寄存器更新,我们定义了模板类 `Module`, 你只需要继承 `Module` 类并传入你定义的结构体即可。 + +例如,定义模块 A 如下: + +```cpp +struct AInput { + Wire<1> ready; + Wire<32> data; +}; +struct AOutput { + Wire<1> valid; + Wire<32> data; +}; +struct AInner { + Register<32> reg; +}; +struct A : Module { + // TODO +}; +``` + +在定义了数据之后,你需要实现模块内部的逻辑。你需要实现 `work` 虚函数,该函数将在每个周期被调用。 + +```cpp +struct A : Module { + void work() override { + // TODO + } +}; +``` + +在实现了模块之后,你可以将模块添加到 `CPU` 类中。 + +```cpp +CPU cpu; +std::unique_ptr a = std::make_unique(); +// TODO: 为 a 连线 +cpu.add_module(a); +``` + +你也可以 +```cpp +CPU cpu; +A a; // a's lifetime should be at least as long as cpu's +cpu.add_module(&a); +``` + +最后,调用 `cpu.run()` 即可开始模拟。 + +为了能够在结束时退出,你可以通过在 `work` 函数中抛出异常来结束模拟。 +当然,你也可以自行修改 work 函数的签名,返回一些信息并在 `run` 函数中判断是否结束模拟。 +或者,你可以在 run 函数中检查某个寄存器的值,这些大家可以自行实现。 + +为了保证正确性,在最终测试中,应当保证模块执行的顺序与运行结果无关。 diff --git a/docs/help.md b/docs/help.md index 05cd6a7..8acaaab 100644 --- a/docs/help.md +++ b/docs/help.md @@ -1,67 +1,68 @@ -# How to use +# How to Use -You need to keep in mind that all the value types just -behave like normal integers, except that we have a similar -bit-width matching check as the verilog integers. -(e.g. 4-bit register can only be assigned from a 4-bit value) +When using this library, it's important to note that all the value types behave like regular integers, with the exception that we have a similar bit-width matching check as with Verilog integers. +(e.g. a 4-bit register can only be assigned from a 4-bit value) -Also, you should use the recommended way to perform the auto-synchronization, -which can (hope so) save you from writing a lot of duplicated code. +Additionally, it is recommended to use the provided method for auto-synchronization, which can potentially save you from writing a lot of duplicated code. ## Requirements -`g++-12` or later. `-std=c++20` or `-std=c++2b`. +You will need `g++-12` or later, with the flags `-std=c++20`. -e.g. `g++ -std=c++2b ...` +Example: `g++ -std=c++20 ...` -## Include the library +Your code may still run on `g++-11` or earlier, but we do not guarantee it. -This is a header-only library, which means you just need to include all your required headers in your project. +## Including the Library -We strongly recommend you to include `include/tools.h` to simply include all the headers. +This is a header-only library, which means you simply need to include all the required headers in your project. + +We strongly recommend including `include/tools.h` to easily include all the headers. ```cpp #include "include/tools.h" ``` -## Debug mode +## Debug Mode -We provide a debug mode, which will perform more checks in the code. To enable that, -just define the macro `_DEBUG` before including the headers. -You may also pass `-D _DEBUG` to the compiling command to define the macro. +We provide a debug mode, which performs additional checks in the code. To enable this, simply define the macro `_DEBUG` before including the headers. +You can also pass `-D _DEBUG` to the compiler to define the macro, or define it directly in your code. ```cpp #define _DEBUG ``` -## Value types +We **strongly recommend** enabling the debug mode when developing your project. -You may at first treat all these types as the verilog integers. -You may assume all the types below support basic arithmetic operations, -and will **clip** the value just as the verilog integers operations. +Example: `g++ -std=c++20 -D _DEBUG ...` + +## Value Types + +Initially, you can treat all these types as Verilog integers. +You can assume that all the types below support basic arithmetic operations and will **clip** the value just like Verilog integer operations. ### Register -Registers are just like the registers in the verilog. +Registers are similar to those in Verilog. -To simulate the registers, a `Register` is only allowed to be assigned once in a cycle. +To simulate registers, a `Register` is only allowed to be assigned once in a cycle. ```cpp // Declare a 32-bit register // The maximum bit-width depends on the max_size_t // Currently, the max_size_t is std::uint32_t Register<32> reg; -reg <= reg + 1; // OK, allow to assign from some value with the same bit-width +reg <= reg + 1; // OK, allows assignment from a value with the same bit-width Register<16> reg2; reg <= reg2 * reg2; // Compile error, the bit-width is different (32 vs 16) ``` ### Wire -Wires are also similar to the wires in the verilog. +Wires are also similar to those in Verilog. -It should be assigned exactly once before reading. -It can accept a function-like input (function pointers/lambdas) to extract the value. +They should be assigned exactly once before reading. +They can accept a function-like input (function pointers/lambdas) to extract the value. ```cpp // Declare a 4-bit wire @@ -80,14 +81,14 @@ Wire <4> wire2 = [®]() -> auto & { return reg; }; // Ill formed! The wire is assigned twice wire = []() { return 0b11010; }; -// Ill formed! Wire can not accept a value -// with different bit-width +// Ill formed! Wire cannot accept a value +// with a different bit-width Wire <5> wire3 = [&]() -> auto & { return reg + 4; }; ``` ### Bit -Bit is an intermediate type, which can be used to represent an integer with some bit_width. +Bit is an intermediate type, which can be used to represent an integer with a specific bit width. ```cpp Bit <5> b = 0b111111; // Clipped to 0b11111 @@ -100,20 +101,18 @@ Bit <4> d = b.slice <4> (1); // Copy 4 bits from bit 1 (bit 4, 3, 2, 1) to d Bit <1> e = d[0]; // Get the 0-th bit of d Bit f = { b + 3, c, d }; // Concatenate b + 3, c, d from high to low - ``` ## Synchronization -We support a feature of auto synchronization, which means that you can -easily synchronize all the members of a class by simply calling the `sync_member` function. +We support a feature of auto synchronization, which means that you can easily synchronize all the members of a class by simply calling the `sync_member` function. We support 4 types of synchronization: 1. Register / Wire type synchronization. 2. An array (only std::array is supported) of synchronizable objects. -3. A class which contains only synchronizable objects, and satisfies std::is_aggregate. -4. A class which has some basis which are synchronizable objects, and has a special tag. +3. A class which contains only synchronizable objects and satisfies std::is_aggregate. +4. A class which has some basis that are synchronizable objects, and has a special tag. We will show some examples of 3 and 4. @@ -155,10 +154,10 @@ void demo() { } ``` -## Common mistakes +## Common Mistakes -Turn to the [mistake](mistake.md) page to see some common mistakes. +Refer to the [mistake](mistake.md) page to see some common mistakes. ## Examples -See demo folder for more examples. +See the demo folder for more examples. diff --git a/docs/mistake.md b/docs/mistake.md index 151e2de..27011c0 100644 --- a/docs/mistake.md +++ b/docs/mistake.md @@ -1,9 +1,9 @@ -# Some common mistakes +# Some Common Mistakes -## Bit-width mismatch +## Bit-width Mismatch -That is, the bit-width of the LHS and RHS of an assignment operation are different. -For example, the following code will result in compile error: +This occurs when the bit-width of the left-hand side (LHS) and right-hand side (RHS) of an assignment operation are different. +For example, the following code will result in a compile error: ```cpp Register <8> r1; @@ -11,32 +11,28 @@ Register <16> r2; r1 <= r2; // Error: bit-width mismatch ``` -## Register/Wire passed by value +## Register/Wire Passed by Value -Register/Wire can be only passed by reference. We forbid -the copy/move constructor for Register/Wire to avoid misuse. +Register/Wire can only be passed by reference. We forbid the copy/move constructor for Register/Wire to prevent misuse. -This may cause some error in the lambda function of a wire. +This may cause errors in the lambda function of a wire. ```cpp Register <8> r1; Wire <8> w1 = [&]() { return r1; }; ``` -To fix this issue, you may return by reference, -or use + operator to convert the value to bit type. +To fix this issue, you may return by reference or use the + operator to convert the value to a bit type. ```cpp Register <8> r1; Wire <8> w1 = [&]() -> auto & { return r1; }; -Wire <8> w2 = [&]() { return +r1; }; +Wire <8> w2 = [&]() { return +r1; }; // +r1 will return Bit<8>b ``` -## C-array as member variable +## C-array as Member Variable -We do not support C-array as member variable for synchronization. -Our C++ static reflection library do not support parsing -C-array as member variable currently. +We do not support C-arrays as member variables for synchronization. Our C++ static reflection library does not currently support parsing C-arrays as member variables. Always use `std::array` instead. @@ -47,6 +43,6 @@ struct NeedToSync { }; ``` -## Some others +## Others -If you encounter some other issues, please feel free to open an issue. +If you encounter other issues, please feel free to open an issue. diff --git a/include/bit.h b/include/bit.h index d50ec7d..5b2f37e 100644 --- a/include/bit.h +++ b/include/bit.h @@ -1,50 +1,50 @@ #pragma once #include "concept.h" #include "debug.h" -#include #include #include +#include namespace dark { -template +template struct Bit { - private: - static_assert(0 < _Nm && _Nm <= kMaxLength, - "Bit: _Nm out of range. Should be in [1, kMaxLength]"); - - max_size_t _M_data : _Nm; // Real storage +private: + static_assert(0 < _Nm && _Nm <= kMaxLength, + "Bit: _Nm out of range. Should be in [1, kMaxLength]"); - template - static constexpr void _M_range_check(); + max_size_t _M_data : _Nm; // Real storage - public: - static constexpr std::size_t _Bit_Len = _Nm; + template + static constexpr void _M_range_check(); - constexpr Bit(max_size_t data = 0) : _M_data(data) {} +public: + static constexpr std::size_t _Bit_Len = _Nm; - constexpr explicit operator max_size_t() const { return this->_M_data; } + constexpr Bit(max_size_t data = 0) : _M_data(data) {} - template - requires ((_Tp::_Bit_Len + ...) == _Nm) - constexpr Bit(const _Tp &...args); + constexpr explicit operator max_size_t() const { return this->_M_data; } - template _Tp> - constexpr Bit &operator=(const _Tp &val); + template + requires((_Tp::_Bit_Len + ...) == _Nm) + constexpr Bit(const _Tp &...args); - template _Tp> - constexpr void set(const _Tp &val); + template _Tp> + constexpr Bit &operator=(const _Tp &val); - template - constexpr auto range() const -> Bit <_Hi - _Lo + 1>; + template _Tp> + constexpr void set(const _Tp &val); - template - constexpr auto slice(std::size_t pos) const -> Bit <_Len>; + template + constexpr auto range() const -> Bit<_Hi - _Lo + 1>; - constexpr Bit <1> operator [](std::size_t pos) const { return this->slice(pos); } + template + constexpr auto slice(std::size_t pos) const -> Bit<_Len>; + + constexpr Bit<1> operator[](std::size_t pos) const { return this->slice(pos); } }; -template +template Bit(_Tp...) -> Bit<(_Tp::_Bit_Len + ...)>; } // namespace dark diff --git a/include/bit_impl.h b/include/bit_impl.h index e67ec17..609bdec 100644 --- a/include/bit_impl.h +++ b/include/bit_impl.h @@ -3,84 +3,87 @@ namespace dark { -template +template constexpr auto int_concat(max_size_t arg) { return arg; } -template -constexpr auto int_concat(max_size_t arg, auto ...args) { - return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...); +template +constexpr auto int_concat(max_size_t arg, auto... args) { + return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...); } -template +template 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); + 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 -constexpr auto sign_extend(const _Tp & val) { - return sign_extend<_Tp::_Bit_Len, _New>(static_cast(val)); +template +constexpr auto sign_extend(const _Tp &val) { + return sign_extend<_Tp::_Bit_Len, _New>(static_cast(val)); } -template +template 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); + 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 +template constexpr auto zero_extend(const _Tp &val) { - return zero_extend<_Tp::_Bit_Len, _New>(static_cast(val)); + return zero_extend<_Tp::_Bit_Len, _New>(static_cast(val)); } -template -template -requires ((_Tp::_Bit_Len + ...) == _Nm) +template +template + requires((_Tp::_Bit_Len + ...) == _Nm) constexpr Bit<_Nm>::Bit(const _Tp &...args) - : _M_data(int_concat<_Tp::_Bit_Len...>(static_cast(args)...)) {} + : _M_data(int_concat<_Tp::_Bit_Len...>(static_cast(args)...)) {} -template -template _Tp> +template +template _Tp> constexpr Bit<_Nm> &Bit<_Nm>::operator=(const _Tp &val) { - this->_M_data = static_cast(val); - return *this; + this->_M_data = static_cast(val); + return *this; } -template -template +template +template 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"); + 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 -template _Tp> +template +template _Tp> constexpr void Bit<_Nm>::set(const _Tp &val) { - this->_M_range_check<_Hi, _Lo>(); - auto data = static_cast(val); - constexpr auto _Length = _Hi - _Lo + 1; - auto mask = make_mask<_Length> << _Lo; - this->_M_data = (this->_M_data & ~mask) | ((data << _Lo) & mask); + this->_M_range_check<_Hi, _Lo>(); + auto data = static_cast(val); + constexpr auto _Length = _Hi - _Lo + 1; + auto mask = make_mask<_Length> << _Lo; + this->_M_data = (this->_M_data & ~mask) | ((data << _Lo) & mask); } -template -template -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 +template +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 -template -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"); - return Bit<_Len>(this->_M_data >> pos); +template +template +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"); + return Bit<_Len>(this->_M_data >> pos); } - } // namespace dark diff --git a/include/concept.h b/include/concept.h index 2145e9f..149cdb4 100644 --- a/include/concept.h +++ b/include/concept.h @@ -1,7 +1,7 @@ #pragma once -#include -#include #include +#include +#include namespace dark { @@ -10,45 +10,45 @@ using max_ssize_t = std::int32_t; static constexpr std::size_t kMaxLength = std::numeric_limits::digits; -template +template consteval max_size_t make_mask() { - static_assert(_Len <= kMaxLength, "Mask length out of range"); - return _Len == kMaxLength ? ~max_size_t(0) : (max_size_t(1) << _Len) - 1; + static_assert(_Len <= kMaxLength, "Mask length out of range"); + return _Len == kMaxLength ? ~max_size_t(0) : (max_size_t(1) << _Len) - 1; } } // namespace dark namespace dark::concepts { -template -using func_t = void(*)(_Tp); +template +using func_t = void (*)(_Tp); -template -concept implicit_convertible_to = requires(_From &a, func_t <_To> b) { - b(a); // Can implicitly convert +template +concept implicit_convertible_to = requires(_From &a, func_t<_To> b) { + b(a); // Can implicitly convert }; -template +template concept explicit_convertible_to = - !implicit_convertible_to <_From, _To> -&& std::constructible_from <_To, _From>; + !implicit_convertible_to<_From, _To> && std::constructible_from<_To, _From>; -template +template concept has_length = requires { { +_Tp::_Bit_Len } -> std::same_as ; }; -template -concept bit_type = has_length <_Tp> && explicit_convertible_to <_Tp, max_size_t>; +template +concept bit_type = has_length<_Tp> && explicit_convertible_to<_Tp, max_size_t>; -template -concept int_type = !has_length <_Tp> && implicit_convertible_to <_Tp, max_size_t>; +template +concept int_type = !has_length<_Tp> && implicit_convertible_to<_Tp, max_size_t>; -template +template concept bit_match = - (bit_type <_Lhs> && bit_type <_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len) -|| (int_type <_Lhs> || int_type <_Rhs>); + (bit_type<_Lhs> && bit_type<_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len) // prevent format + || (int_type<_Lhs> && bit_type<_Rhs>) // + || (bit_type<_Lhs> && int_type<_Rhs>); -template +template concept bit_convertible = - (bit_type <_Tp> && _Tp::_Bit_Len == _Len) || int_type <_Tp>; + (bit_type<_Tp> && _Tp::_Bit_Len == _Len) || int_type<_Tp>; } // namespace dark::concepts diff --git a/include/cpu.h b/include/cpu.h new file mode 100644 index 0000000..732d4b8 --- /dev/null +++ b/include/cpu.h @@ -0,0 +1,63 @@ +#pragma once +#include "module.h" +#include +#include +#include +#include + +namespace dark { + +class CPU { +private: + std::vector> mod_owned; + std::vector modules; + +public: + unsigned long long cycles = 0; + +private: + void sync_all() { + for (auto &module: modules) + module->sync(); + } + +public: + /// @attention the pointer will be moved. you SHOULD NOT use it after calling this function. + template + requires std::derived_from<_Tp, ModuleBase> + void add_module(std::unique_ptr<_Tp> &module) { + modules.push_back(module.get()); + mod_owned.emplace_back(std::move(module)); + } + void add_module(std::unique_ptr module) { + modules.push_back(module.get()); + mod_owned.emplace_back(std::move(module)); + } + void add_module(ModuleBase *module) { + modules.push_back(module); + } + + void run_once() { + ++cycles; + for (auto &module: modules) + module->work(); + sync_all(); + } + void run_once_shuffle() { + static std::default_random_engine engine; + std::vector shuffled = modules; + std::shuffle(shuffled.begin(), shuffled.end(), engine); + + ++cycles; + for (auto &module: shuffled) + module->work(); + sync_all(); + } + void run(unsigned long long max_cycles = 0, bool shuffle = false) { + auto func = shuffle ? &CPU::run_once_shuffle : &CPU::run_once; + while (max_cycles == 0 || cycles < max_cycles) + (this->*func)(); + } +}; + +} // namespace dark diff --git a/include/debug.h b/include/debug.h index 01f4e53..dad80be 100644 --- a/include/debug.h +++ b/include/debug.h @@ -8,60 +8,64 @@ namespace dark::debug { /** - * Copied from https://en.cppreference.com/w/cpp/utility/unreachable + * 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(); + 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(); + // 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 +template struct assert { #ifdef _DEBUG - 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); - } + 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); + } #else - explicit assert(_Tp &&, _Args &&...) {} + explicit assert(_Tp &&, _Args &&...) {} #endif }; -template +template assert(_Tp &&, _Args &&...) -> assert<_Tp, _Args...>; -template +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; } +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 */ } +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; } +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/module.h b/include/module.h new file mode 100644 index 0000000..6ed7ea5 --- /dev/null +++ b/include/module.h @@ -0,0 +1,27 @@ +#pragma once +#include "synchronize.h" +namespace dark { + +namespace details { + struct empty_class { + void sync() { /* do nothing */ } + }; +} // namespace details + +struct ModuleBase { + virtual void work() = 0; + virtual void sync() = 0; + virtual ~ModuleBase() = default; +}; + +template + requires std::is_aggregate_v<_Tinput> && std::is_aggregate_v<_Toutput> && std::is_aggregate_v<_Tprivate> +struct Module : public ModuleBase, public _Tinput, public _Toutput, protected _Tprivate { + void sync() override final { + sync_member(static_cast<_Tinput &>(*this)); + sync_member(static_cast<_Toutput &>(*this)); + sync_member(static_cast<_Tprivate &>(*this)); + } +}; + +} // namespace dark diff --git a/include/operator.h b/include/operator.h index 654d50b..23bb701 100644 --- a/include/operator.h +++ b/include/operator.h @@ -3,127 +3,128 @@ namespace dark { +using dark::concepts::bit_match; using dark::concepts::bit_type; using dark::concepts::int_type; -using dark::concepts::bit_match; -template -constexpr auto cast(const _Tp& value) { - return static_cast (value); +template +constexpr auto cast(const _Tp &value) { + return static_cast(value); } -template +template consteval auto get_common_length() -> std::size_t { - static_assert(bit_match <_Tp, _Up>); - if constexpr (bit_type <_Tp>) { - return _Tp::_Bit_Len; - } else { - static_assert(bit_type <_Up>, "Invalid common length"); - return _Up::_Bit_Len; - } + static_assert(bit_match<_Tp, _Up>); + if constexpr (bit_type<_Tp>) { + return _Tp::_Bit_Len; + } + else { + static_assert(bit_type<_Up>, "Invalid common length"); + return _Up::_Bit_Len; + } } -template -requires bit_match <_Tp, _Up> -constexpr auto operator + (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) + cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator+(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) + cast(rhs)); } -template -requires bit_match <_Tp, _Up> -constexpr auto operator - (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) - cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator-(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) - cast(rhs)); } -template -requires bit_match <_Tp, _Up> -constexpr auto operator * (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) * cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator*(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) * cast(rhs)); } -template -requires bit_match <_Tp, _Up> -constexpr auto operator / (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) / cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator/(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) / cast(rhs)); } -template -requires bit_match <_Tp, _Up> -constexpr auto operator & (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) & cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator&(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) & cast(rhs)); } -template -requires bit_match <_Tp, _Up> -constexpr auto operator | (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) | cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator|(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) | cast(rhs)); } -template -requires bit_match <_Tp, _Up> -constexpr auto operator ^ (const _Tp &lhs, const _Up &rhs) { - constexpr auto _Len = get_common_length <_Tp, _Up>(); - return Bit <_Len> (cast(lhs) ^ cast(rhs)); +template + requires bit_match<_Tp, _Up> +constexpr auto operator^(const _Tp &lhs, const _Up &rhs) { + constexpr auto _Len = get_common_length<_Tp, _Up>(); + return Bit<_Len>(cast(lhs) ^ cast(rhs)); } -template -concept int_or_bit = int_type <_Tp> || bit_type <_Tp>; +template +concept int_or_bit = int_type<_Tp> || bit_type<_Tp>; -template -constexpr auto operator << (const _Tp &lhs, const _Up &rhs) { - return Bit <_Tp::_Bit_Len> (cast(lhs) << (cast(rhs) & kMaxLength)); +template +constexpr auto operator<<(const _Tp &lhs, const _Up &rhs) { + return Bit<_Tp::_Bit_Len>(cast(lhs) << (cast(rhs) & kMaxLength)); } -template -constexpr auto operator >> (const _Tp &lhs, const _Up &rhs) { - return Bit <_Tp::_Bit_Len> (cast(lhs) >> (cast(rhs) & kMaxLength)); +template +constexpr auto operator>>(const _Tp &lhs, const _Up &rhs) { + return Bit<_Tp::_Bit_Len>(cast(lhs) >> (cast(rhs) & kMaxLength)); } -template -constexpr auto operator ~ (const _Tp &value) { - return Bit <_Tp::_Bit_Len> (~cast(value)); +template +constexpr auto operator~(const _Tp &value) { + return Bit<_Tp::_Bit_Len>(~cast(value)); } -template -constexpr auto operator ! (const _Tp &value) { - return ~value; +template +constexpr auto operator!(const _Tp &value) { + return ~value; } -template -constexpr auto operator + (const _Tp &value) { - return Bit <_Tp::_Bit_Len> (+cast(value)); +template +constexpr auto operator+(const _Tp &value) { + return Bit<_Tp::_Bit_Len>(+cast(value)); } -template -constexpr auto operator - (const _Tp &value) { - return Bit <_Tp::_Bit_Len> (-cast(value)); +template +constexpr auto operator-(const _Tp &value) { + return Bit<_Tp::_Bit_Len>(-cast(value)); } -template -constexpr bool operator && (const _Tp &lhs, const _Up &rhs) { - return cast(lhs) && cast(rhs); +template +constexpr bool operator&&(const _Tp &lhs, const _Up &rhs) { + return cast(lhs) && cast(rhs); } -template -constexpr bool operator || (const _Tp &lhs, const _Up &rhs) { - return cast(lhs) || cast(rhs); +template +constexpr bool operator||(const _Tp &lhs, const _Up &rhs) { + return cast(lhs) || cast(rhs); } -template -constexpr bool operator == (const _Tp &lhs, const _Up &rhs) { - return cast(lhs) == cast(rhs); +template +constexpr bool operator==(const _Tp &lhs, const _Up &rhs) { + return cast(lhs) == cast(rhs); } -template -constexpr auto operator <=> (const _Tp &lhs, const _Up &rhs) { - return cast(lhs) <=> cast(rhs); +template +constexpr auto operator<=>(const _Tp &lhs, const _Up &rhs) { + return cast(lhs) <=> cast(rhs); } diff --git a/include/reflect.h b/include/reflect.h index d3a1dfd..90bd065 100644 --- a/include/reflect.h +++ b/include/reflect.h @@ -1,81 +1,104 @@ #pragma once -#include #include +#include namespace dark::reflect { /* A init helper to get the size of a struct. */ -struct init_helper { template operator _Tp(); }; +struct init_helper { + template + operator _Tp(); +}; /* A size helper to get the size of a struct. */ -template requires std::is_aggregate_v <_Tp> +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 {}); - } + 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(_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> +inline consteval auto member_size() -> std::size_t { return member_size_aux<_Tp>(); } -template requires std::is_aggregate_v <_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."); - } + 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/register.h b/include/register.h index 760fa3a..aa27df5 100644 --- a/include/register.h +++ b/include/register.h @@ -1,48 +1,47 @@ #pragma once -#include "debug.h" #include "concept.h" +#include "debug.h" namespace dark { -template +template struct Register { - private: - static_assert(0 < _Len && _Len <= kMaxLength, - "Register: _Len must be in range [1, kMaxLength]."); +private: + static_assert(0 < _Len && _Len <= kMaxLength, + "Register: _Len must be in range [1, kMaxLength]."); - friend class Visitor; + friend class Visitor; - max_size_t _M_old : _Len; - max_size_t _M_new : _Len; + max_size_t _M_old : _Len; + max_size_t _M_new : _Len; - [[no_unique_address]] - debug::DebugValue _M_assigned; + [[no_unique_address]] + debug::DebugValue _M_assigned; - void sync() { - if (this->_M_assigned) { - this->_M_assigned = false; - this->_M_old = this->_M_new; - } - } + void sync() { + this->_M_assigned = false; + this->_M_old = this->_M_new; + } - public: - static constexpr std::size_t _Bit_Len = _Len; +public: + static constexpr std::size_t _Bit_Len = _Len; - Register() : _M_old(), _M_new(), _M_assigned() {} + Register() : _M_old(), _M_new(), _M_assigned() {} - Register(Register &&) = delete; - Register(const Register &) = delete; - Register &operator=(Register &&) = delete; - Register &operator=(const Register &rhs) = delete; + Register(Register &&) = delete; + Register(const Register &) = delete; + Register &operator=(Register &&) = delete; + Register &operator=(const Register &rhs) = delete; - template _Tp> - void operator <= (const _Tp &value) { - debug::assert(!this->_M_assigned, "Register is double assigned in this cycle."); - this->_M_assigned = true; - this->_M_new = static_cast (value); - } + template _Tp> + void operator<=(const _Tp &value) { + debug::assert(!this->_M_assigned, "Register is double assigned in this cycle."); + this->_M_assigned = true; + this->_M_new = static_cast(value); + } - explicit operator max_size_t() const { return this->_M_old; } + explicit operator max_size_t() const { return this->_M_old; } + explicit operator bool() const { return this->_M_old; } }; } // namespace dark diff --git a/include/synchronize.h b/include/synchronize.h index b3decd5..af67710 100644 --- a/include/synchronize.h +++ b/include/synchronize.h @@ -5,56 +5,62 @@ namespace dark { struct Visitor { - template - static constexpr bool is_syncable_v = - requires(_Tp &val) { { val.sync() } -> std::same_as; }; + 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 + requires is_syncable_v<_Tp> + static void sync(_Tp &val) { val.sync(); } - template - static _Base &cast(_Tp &value) { return static_cast<_Base &>(value); } + template + static _Base &cast(_Tp &value) { return static_cast<_Base &>(value); } }; -template +template struct SyncTags {}; -template +template static constexpr bool is_valid_tag_v = false; -template +template static constexpr bool is_valid_tag_v> = true; -template +template concept has_valid_tag = is_valid_tag_v; -template +template static constexpr bool is_std_array_v = std::is_array_v<_Tp>; -template +template static constexpr bool is_std_array_v> = true; -template +template inline void sync_member(_Tp &value); -template +template inline void sync_by_tag(_Tp &value, SyncTags<_Base...>) { - (sync_member(Visitor::cast<_Tp, _Base>(value)), ...); + (sync_member(Visitor::cast<_Tp, _Base>(value)), ...); } -template +template inline void sync_member(_Tp &value) { - if constexpr (std::is_const_v <_Tp>) { - /* Do nothing! Constant members need no synchronization! */ - } else 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."); - } + if constexpr (std::is_const_v<_Tp>) { + /* Do nothing! Constant members need no synchronization! */ + } + else 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 +} // namespace dark diff --git a/include/tools.h b/include/tools.h index 8a01306..7f273a2 100644 --- a/include/tools.h +++ b/include/tools.h @@ -1,31 +1,33 @@ #pragma once #include "bit.h" #include "bit_impl.h" -#include "wire.h" +#include "operator.h" #include "register.h" #include "synchronize.h" -#include "operator.h" +#include "wire.h" +#include "module.h" +#include "cpu.h" using dark::Bit; using dark::sign_extend; using dark::zero_extend; -using dark::Wire; using dark::Register; +using dark::Wire; -using dark::SyncTags; using dark::sync_member; +using dark::SyncTags; using dark::Visitor; using dark::max_size_t; using dark::max_ssize_t; -template +template constexpr auto to_unsigned(const _Tp &x) { - return static_cast(x); + return static_cast(x); } -template +template constexpr auto to_signed(const _Tp &x) { - return static_cast(to_unsigned(x)); + return static_cast(sign_extend(x)); } diff --git a/include/wire.h b/include/wire.h index 7074e34..5367df4 100644 --- a/include/wire.h +++ b/include/wire.h @@ -1,113 +1,116 @@ #pragma once -#include "debug.h" #include "concept.h" +#include "debug.h" #include namespace dark { namespace details { -template -concept WireFunction = - concepts::bit_convertible >, _Len>; + template + concept WireFunction = + concepts::bit_convertible>, _Len>; -struct FuncBase { - using _Ret_t = max_size_t; - using _Cpy_t = FuncBase *; - virtual _Ret_t call() const = 0; - virtual _Cpy_t copy() const = 0; - virtual ~FuncBase() = default; -}; + struct FuncBase { + using _Ret_t = max_size_t; + using _Cpy_t = FuncBase *; + virtual _Ret_t call() const = 0; + virtual _Cpy_t copy() const = 0; + virtual ~FuncBase() = default; + }; -template _Fn> -struct FuncImpl final : FuncBase { - _Fn _M_lambda; + template _Fn> + struct FuncImpl final : FuncBase { + _Fn _M_lambda; - template - FuncImpl(_Tp &&fn) : _M_lambda(std::forward <_Tp> (fn)) {} + template + FuncImpl(_Tp &&fn) : _M_lambda(std::forward<_Tp>(fn)) {} - _Ret_t call() const override { return static_cast <_Ret_t> (this->_M_lambda()); } - _Cpy_t copy() const override { return new FuncImpl(*this); } -}; + _Ret_t call() const override { return static_cast<_Ret_t>(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; } -}; + 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 +template struct Wire { - private: - static_assert(0 < _Len && _Len <= kMaxLength, - "Wire: _Len must be in range [1, kMaxLength]."); +private: + static_assert(0 < _Len && _Len <= kMaxLength, + "Wire: _Len must be in range [1, kMaxLength]."); - friend class Visitor; + friend class Visitor; - using _Manage_t = std::unique_ptr ; + using _Manage_t = std::unique_ptr; - _Manage_t _M_func; + _Manage_t _M_func; - mutable max_size_t _M_cache : _Len; - mutable bool _M_holds; + mutable max_size_t _M_cache : _Len; + mutable bool _M_holds; - [[no_unique_address]] - debug::DebugValue _M_assigned; + [[no_unique_address]] + debug::DebugValue _M_assigned; - void sync() { this->_M_holds = false; } +private: + void sync() { this->_M_holds = false; } - template _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)}; - } + template _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; - } + void _M_checked_assign() { + debug::assert(!this->_M_assigned, "Wire is assigned twice."); + this->_M_assigned = true; + } - public: - static constexpr std::size_t _Bit_Len = _Len; +public: + static constexpr std::size_t _Bit_Len = _Len; - Wire() : - _M_func(new details::EmptyWire), - _M_cache(), _M_holds(), _M_assigned() {} + 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; - this->_M_cache = this->_M_func->call(); - } - return this->_M_cache; - } + explicit operator max_size_t() const { + if (this->_M_holds == false) { + this->_M_holds = true; + this->_M_cache = this->_M_func->call(); + } + return this->_M_cache; + } - Wire(Wire &&) = delete; - Wire(const Wire &) = delete; - Wire &operator=(Wire &&) = delete; - Wire &operator=(const Wire &rhs) = delete; + Wire(Wire &&) = delete; + Wire(const Wire &) = delete; + Wire &operator=(Wire &&) = delete; + Wire &operator=(const Wire &rhs) = delete; - template _Fn> - Wire(_Fn &&fn) : - _M_func(_M_new_func(std::forward <_Fn> (fn))), - _M_cache(), _M_holds(), _M_assigned() {} + template _Fn> + Wire(_Fn &&fn) : _M_func(_M_new_func(std::forward<_Fn>(fn))), + _M_cache(), _M_holds(), _M_assigned() {} - template _Fn> - Wire &operator=(_Fn &&fn) { - return this->assign(std::forward <_Fn> (fn)), *this; - } + template _Fn> + Wire &operator=(_Fn &&fn) { + return this->assign(std::forward<_Fn>(fn)), *this; + } - template _Fn> - void assign(_Fn &&fn) { - this->_M_checked_assign(); - this->_M_func.reset(_M_new_func(std::forward <_Fn> (fn))); - this->sync(); - } + template _Fn> + void assign(_Fn &&fn) { + this->_M_checked_assign(); + this->_M_func.reset(_M_new_func(std::forward<_Fn>(fn))); + this->sync(); + } + + explicit operator bool() const { + return static_cast(*this); + } };