Merge branch 'DarkSharpness:main' into main

This commit is contained in:
2024-07-24 09:50:39 +08:00
committed by GitHub
17 changed files with 768 additions and 587 deletions

12
CMakeLists.txt Normal file
View File

@ -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)

View File

@ -1,131 +1,111 @@
#include "../include/tools.h" #include "tools.h"
#include <iostream> #include <iostream>
#include <unordered_map>
bool reset;
bool ready;
// RISC-V // RISC-V
enum class Opcode : dark::max_size_t { enum class Opcode : dark::max_size_t {
ADD, ADD,
SUB, SUB,
SLL, SLL,
SRL, SRL,
SRA, SRA,
AND, AND,
OR, OR,
XOR, XOR,
SLT, SLT,
SLTU, SLTU,
SGE, SGE,
SGEU, SGEU,
SEQ, SEQ,
SNEQ SNEQ
}; };
// Normally, only wire can be used in the input. // Normally, only wire can be used in the input.
struct AluInput { struct AluInput {
Wire <8> opcode; Wire<8> opcode;
Wire <1> issue; Wire<1> issue;
Wire <32> rs1; Wire<32> rs1;
Wire <32> rs2; Wire<32> rs2;
}; };
struct AluOutput { struct AluOutput {
Register <32> out; Register<32> out;
Register <1> done; Register<1> done;
}; };
struct AluModule : AluInput, AluOutput { struct AluModule : dark::Module<AluInput, AluOutput> {
using Tags = SyncTags<AluInput, AluOutput>; void work() override {
if (issue) {
void work() { switch (static_cast<Opcode>(static_cast<unsigned>(opcode))) {
if (reset) { using enum Opcode;
done <= 0; case ADD: out <= (rs1 + rs2); break;
} else if(ready && issue) { case SUB: out <= (rs1 - rs2); break;
switch (static_cast <Opcode> (static_cast <unsigned> (opcode))) { case SLL: out <= (rs1 << rs2); break;
using enum Opcode; case SRL: out <= (rs1 >> rs2); break;
case ADD: out <= (rs1 + rs2); break; case SRA: out <= (to_signed(rs1) >> to_unsigned(rs2));
case SUB: out <= (rs1 - rs2); break; case AND: out <= (rs1 & rs2); break;
case SLL: out <= (rs1 << rs2); break; case OR: out <= (rs1 | rs2); break;
case SRL: out <= (rs1 >> rs2); break; case XOR: out <= (rs1 ^ rs2); break;
case SRA: out <= (to_signed(rs1) >> to_unsigned(rs2)); case SLT: out <= (to_signed(rs1) < to_signed(rs2)); break;
case AND: out <= (rs1 & rs2); break; case SLTU: out <= (rs1 < rs2); break;
case OR: out <= (rs1 | rs2); break; case SGE: out <= (to_signed(rs1) >= to_signed(rs2)); break;
case XOR: out <= (rs1 ^ rs2); break; case SGEU: out <= (rs1 >= rs2); break;
case SLT: out <= (to_signed(rs1) < to_signed(rs2)); break; case SEQ: out <= (rs1 == rs2); break;
case SLTU: out <= (rs1 < rs2); break; case SNEQ: out <= (rs1 != rs2); break;
case SGE: out <= (to_signed(rs1) >= to_signed(rs2)); break; default: dark::debug::assert(false, "Invalid opcode");
case SGEU: out <= (rs1 >= rs2); break; }
case SEQ: out <= (rs1 == rs2); break; done <= 1;
case SNEQ: out <= (rs1 != rs2); break; }
default: dark::debug::assert(false, "Invalid opcode"); else {
} done <= 0;
done <= 1; }
} else { }
done <= 0;
}
}
}; };
signed main() { int main() {
AluModule alu; 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; dark::CPU cpu;
max_size_t issue; AluModule alu;
max_size_t rs1; alu.opcode = [&]() { return opcode; };
max_size_t rs2; alu.issue = [&]() { return issue; };
alu.rs1 = [&]() { return rs1; };
ready = 1; alu.rs2 = [&]() { return rs2; };
cpu.add_module(&alu);
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 <max_size_t> (Opcode::ADD);
} else if (opstring == "sub") {
opcode = static_cast <max_size_t> (Opcode::SUB);
} else if (opstring == "sll") {
opcode = static_cast <max_size_t> (Opcode::SLL);
} else if (opstring == "srl") {
opcode = static_cast <max_size_t> (Opcode::SRL);
} else if (opstring == "sra") {
opcode = static_cast <max_size_t> (Opcode::SRA);
} else if (opstring == "and") {
opcode = static_cast <max_size_t> (Opcode::AND);
} else if (opstring == "or") {
opcode = static_cast <max_size_t> (Opcode::OR);
} else if (opstring == "xor") {
opcode = static_cast <max_size_t> (Opcode::XOR);
} else if (opstring == "slt") {
opcode = static_cast <max_size_t> (Opcode::SLT);
} else if (opstring == "sltu") {
opcode = static_cast <max_size_t> (Opcode::SLTU);
} else if (opstring == "sge") {
opcode = static_cast <max_size_t> (Opcode::SGE);
} else if (opstring == "sgeu") {
opcode = static_cast <max_size_t> (Opcode::SGEU);
} else if (opstring == "seq") {
opcode = static_cast <max_size_t> (Opcode::SEQ);
} else if (opstring == "sneq") {
opcode = static_cast <max_size_t> (Opcode::SNEQ);
} else {
std::cout << "Invalid opcode" << std::endl;
issue = 0;
}
alu.work();
std::cout << to_unsigned(alu.out) << std::endl;
sync_member(alu);
}
std::unordered_map<std::string, Opcode> 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<max_size_t>(cmd2op[opstring]);
cpu.run_once();
std::cout << "out: " << static_cast<unsigned int>(alu.out) << std::endl;
std::cout << "done: " << static_cast<unsigned int>(alu.done) << std::endl;
}
return 0;
} }

63
docs/frame.md Normal file
View File

@ -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<AInput, AOutput, AInner> {
// TODO
};
```
在定义了数据之后,你需要实现模块内部的逻辑。你需要实现 `work` 虚函数,该函数将在每个周期被调用。
```cpp
struct A : Module<AInput, AOutput, AInner> {
void work() override {
// TODO
}
};
```
在实现了模块之后,你可以将模块添加到 `CPU` 类中。
```cpp
CPU cpu;
std::unique_ptr<A> a = std::make_unique<A>();
// 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 函数中检查某个寄存器的值,这些大家可以自行实现。
为了保证正确性,在最终测试中,应当保证模块执行的顺序与运行结果无关。

View File

@ -1,67 +1,68 @@
# How to use # How to Use
You need to keep in mind that all the value types just 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.
behave like normal integers, except that we have a similar (e.g. a 4-bit register can only be assigned from a 4-bit value)
bit-width matching check as the verilog integers.
(e.g. 4-bit register can only be assigned from a 4-bit value)
Also, you should use the recommended way to perform the auto-synchronization, Additionally, it is recommended to use the provided method for auto-synchronization, which can potentially save you from writing a lot of duplicated code.
which can (hope so) save you from writing a lot of duplicated code.
## Requirements ## 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 ```cpp
#include "include/tools.h" #include "include/tools.h"
``` ```
## Debug mode ## Debug Mode
We provide a debug mode, which will perform more checks in the code. To enable that, We provide a debug mode, which performs additional checks in the code. To enable this, simply define the macro `_DEBUG` before including the headers.
just 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.
You may also pass `-D _DEBUG` to the compiling command to define the macro.
```cpp ```cpp
#define _DEBUG #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. Example: `g++ -std=c++20 -D _DEBUG ...`
You may assume all the types below support basic arithmetic operations,
and will **clip** the value just as the verilog integers operations. ## 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 ### 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 ```cpp
// Declare a 32-bit register // Declare a 32-bit register
// The maximum bit-width depends on the max_size_t // The maximum bit-width depends on the max_size_t
// Currently, the max_size_t is std::uint32_t // Currently, the max_size_t is std::uint32_t
Register<32> reg; 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; Register<16> reg2;
reg <= reg2 * reg2; // Compile error, the bit-width is different (32 vs 16) reg <= reg2 * reg2; // Compile error, the bit-width is different (32 vs 16)
``` ```
### Wire ### 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. They should be assigned exactly once before reading.
It can accept a function-like input (function pointers/lambdas) to extract the value. They can accept a function-like input (function pointers/lambdas) to extract the value.
```cpp ```cpp
// Declare a 4-bit wire // Declare a 4-bit wire
@ -80,14 +81,14 @@ Wire <4> wire2 = [&reg]() -> auto & { return reg; };
// Ill formed! The wire is assigned twice // Ill formed! The wire is assigned twice
wire = []() { return 0b11010; }; wire = []() { return 0b11010; };
// Ill formed! Wire can not accept a value // Ill formed! Wire cannot accept a value
// with different bit-width // with a different bit-width
Wire <5> wire3 = [&]() -> auto & { return reg + 4; }; Wire <5> wire3 = [&]() -> auto & { return reg + 4; };
``` ```
### Bit ### 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 ```cpp
Bit <5> b = 0b111111; // Clipped to 0b11111 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 <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 Bit f = { b + 3, c, d }; // Concatenate b + 3, c, d from high to low
``` ```
## Synchronization ## Synchronization
We support a feature of auto synchronization, which means that you can 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.
easily synchronize all the members of a class by simply calling the `sync_member` function.
We support 4 types of synchronization: We support 4 types of synchronization:
1. Register / Wire type synchronization. 1. Register / Wire type synchronization.
2. An array (only std::array is supported) of synchronizable objects. 2. An array (only std::array is supported) of synchronizable objects.
3. A class which contains only synchronizable objects, and satisfies std::is_aggregate. 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. 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. 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 ## Examples
See demo folder for more examples. See the demo folder for more examples.

View File

@ -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. 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 compile error: For example, the following code will result in a compile error:
```cpp ```cpp
Register <8> r1; Register <8> r1;
@ -11,32 +11,28 @@ Register <16> r2;
r1 <= r2; // Error: bit-width mismatch 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 Register/Wire can only be passed by reference. We forbid the copy/move constructor for Register/Wire to prevent misuse.
the copy/move constructor for Register/Wire to avoid 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 ```cpp
Register <8> r1; Register <8> r1;
Wire <8> w1 = [&]() { return r1; }; Wire <8> w1 = [&]() { return r1; };
``` ```
To fix this issue, you may return by reference, To fix this issue, you may return by reference or use the + operator to convert the value to a bit type.
or use + operator to convert the value to bit type.
```cpp ```cpp
Register <8> r1; Register <8> r1;
Wire <8> w1 = [&]() -> auto & { return 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. 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.
Our C++ static reflection library do not support parsing
C-array as member variable currently.
Always use `std::array` instead. 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.

View File

@ -1,50 +1,50 @@
#pragma once #pragma once
#include "concept.h" #include "concept.h"
#include "debug.h" #include "debug.h"
#include <version>
#include <climits> #include <climits>
#include <concepts> #include <concepts>
#include <version>
namespace dark { namespace dark {
template <std::size_t _Nm> template<std::size_t _Nm>
struct Bit { struct Bit {
private: private:
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]");
max_size_t _M_data : _Nm; // Real storage max_size_t _M_data : _Nm; // Real storage
template <std::size_t _Hi, std::size_t _Lo> template<std::size_t _Hi, std::size_t _Lo>
static constexpr void _M_range_check(); 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(max_size_t data = 0) : _M_data(data) {} constexpr Bit(max_size_t data = 0) : _M_data(data) {}
constexpr explicit operator max_size_t() const { return this->_M_data; } constexpr explicit operator max_size_t() const { return this->_M_data; }
template <concepts::bit_type ..._Tp> template<concepts::bit_type... _Tp>
requires ((_Tp::_Bit_Len + ...) == _Nm) requires((_Tp::_Bit_Len + ...) == _Nm)
constexpr Bit(const _Tp &...args); constexpr Bit(const _Tp &...args);
template <concepts::bit_convertible <_Nm> _Tp> template<concepts::bit_convertible<_Nm> _Tp>
constexpr Bit &operator=(const _Tp &val); constexpr Bit &operator=(const _Tp &val);
template <std::size_t _Hi, std::size_t _Lo = _Hi, concepts::bit_convertible <_Nm> _Tp> template<std::size_t _Hi, std::size_t _Lo = _Hi, concepts::bit_convertible<_Nm> _Tp>
constexpr void set(const _Tp &val); constexpr void set(const _Tp &val);
template <std::size_t _Hi, std::size_t _Lo = _Hi> template<std::size_t _Hi, std::size_t _Lo = _Hi>
constexpr auto range() const -> Bit <_Hi - _Lo + 1>; constexpr auto range() const -> Bit<_Hi - _Lo + 1>;
template <std::size_t _Len = 1> template<std::size_t _Len = 1>
constexpr auto slice(std::size_t pos) const -> Bit <_Len>; constexpr auto slice(std::size_t pos) const -> Bit<_Len>;
constexpr Bit <1> operator [](std::size_t pos) const { return this->slice(pos); } constexpr Bit<1> operator[](std::size_t pos) const { return this->slice(pos); }
}; };
template <concepts::bit_type ..._Tp> template<concepts::bit_type... _Tp>
Bit(_Tp...) -> Bit<(_Tp::_Bit_Len + ...)>; Bit(_Tp...) -> Bit<(_Tp::_Bit_Len + ...)>;
} // namespace dark } // namespace dark

View File

@ -3,84 +3,87 @@
namespace dark { namespace dark {
template <int _First> template<int _First>
constexpr auto int_concat(max_size_t arg) { return arg; } constexpr auto int_concat(max_size_t arg) { return arg; }
template <int _First, int ..._Lens> template<int _First, int... _Lens>
constexpr auto int_concat(max_size_t arg, auto ...args) { constexpr auto int_concat(max_size_t arg, auto... args) {
return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...); return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...);
} }
template <std::size_t _Old, std::size_t _New = kMaxLength> template<std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto sign_extend(max_size_t val) { constexpr auto sign_extend(max_size_t val) {
static_assert(_Old < _New, "sign_extend: _Old should be less than _New"); static_assert(_Old < _New, "sign_extend: _Old should be less than _New");
struct { max_ssize_t _M_data : _Old; } tmp; struct {
return Bit<_New>(tmp._M_data = val); max_ssize_t _M_data : _Old;
} tmp;
return Bit<_New>(tmp._M_data = val);
} }
template <std::size_t _New = kMaxLength, concepts::bit_type _Tp> template<std::size_t _New = kMaxLength, concepts::bit_type _Tp>
constexpr auto sign_extend(const _Tp & val) { constexpr auto sign_extend(const _Tp &val) {
return sign_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val)); return sign_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val));
} }
template <std::size_t _Old, std::size_t _New = kMaxLength> template<std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto zero_extend(max_size_t val) { constexpr auto zero_extend(max_size_t val) {
static_assert(_Old < _New, "zero_extend: _Old should be less than _New"); static_assert(_Old < _New, "zero_extend: _Old should be less than _New");
struct { max_size_t _M_data : _Old; } tmp; struct {
return Bit<_New>(tmp._M_data = val); max_size_t _M_data : _Old;
} tmp;
return Bit<_New>(tmp._M_data = val);
} }
template <std::size_t _New = kMaxLength, concepts::bit_type _Tp> template<std::size_t _New = kMaxLength, concepts::bit_type _Tp>
constexpr auto zero_extend(const _Tp &val) { constexpr auto zero_extend(const _Tp &val) {
return zero_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val)); return zero_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val));
} }
template <std::size_t _Nm> template<std::size_t _Nm>
template <concepts::bit_type ..._Tp> template<concepts::bit_type... _Tp>
requires ((_Tp::_Bit_Len + ...) == _Nm) requires((_Tp::_Bit_Len + ...) == _Nm)
constexpr Bit<_Nm>::Bit(const _Tp &...args) constexpr Bit<_Nm>::Bit(const _Tp &...args)
: _M_data(int_concat<_Tp::_Bit_Len...>(static_cast<max_size_t>(args)...)) {} : _M_data(int_concat<_Tp::_Bit_Len...>(static_cast<max_size_t>(args)...)) {}
template <std::size_t _Nm> template<std::size_t _Nm>
template <concepts::bit_convertible <_Nm> _Tp> template<concepts::bit_convertible<_Nm> _Tp>
constexpr Bit<_Nm> &Bit<_Nm>::operator=(const _Tp &val) { constexpr Bit<_Nm> &Bit<_Nm>::operator=(const _Tp &val) {
this->_M_data = static_cast<max_size_t>(val); this->_M_data = static_cast<max_size_t>(val);
return *this; return *this;
} }
template <std::size_t _Nm> template<std::size_t _Nm>
template <std::size_t _Hi, std::size_t _Lo> template<std::size_t _Hi, std::size_t _Lo>
constexpr void Bit<_Nm>::_M_range_check() { constexpr void Bit<_Nm>::_M_range_check() {
static_assert(_Lo <= _Hi, "Bit::range_check: _Lo should be no greater than _Hi"); 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(_Hi < _Nm, "Bit::range_check: _Hi should be less than _Nm");
} }
template <std::size_t _Nm> template<std::size_t _Nm>
template <std::size_t _Hi, std::size_t _Lo, concepts::bit_convertible <_Nm> _Tp> template<std::size_t _Hi, std::size_t _Lo, concepts::bit_convertible<_Nm> _Tp>
constexpr void Bit<_Nm>::set(const _Tp &val) { constexpr void Bit<_Nm>::set(const _Tp &val) {
this->_M_range_check<_Hi, _Lo>(); this->_M_range_check<_Hi, _Lo>();
auto data = static_cast<max_size_t>(val); auto data = static_cast<max_size_t>(val);
constexpr auto _Length = _Hi - _Lo + 1; constexpr auto _Length = _Hi - _Lo + 1;
auto mask = make_mask<_Length> << _Lo; auto mask = make_mask<_Length> << _Lo;
this->_M_data = (this->_M_data & ~mask) | ((data << _Lo) & mask); this->_M_data = (this->_M_data & ~mask) | ((data << _Lo) & mask);
} }
template <std::size_t _Nm> template<std::size_t _Nm>
template <std::size_t _Hi, std::size_t _Lo> template<std::size_t _Hi, std::size_t _Lo>
constexpr auto Bit<_Nm>::range() const -> Bit <_Hi - _Lo + 1> { constexpr auto Bit<_Nm>::range() const -> Bit<_Hi - _Lo + 1> {
this->_M_range_check<_Hi, _Lo>(); this->_M_range_check<_Hi, _Lo>();
constexpr auto _Length = _Hi - _Lo + 1; constexpr auto _Length = _Hi - _Lo + 1;
return Bit<_Length>(this->_M_data >> _Lo); return Bit<_Length>(this->_M_data >> _Lo);
} }
template <std::size_t _Nm> template<std::size_t _Nm>
template <std::size_t _Len> template<std::size_t _Len>
constexpr auto Bit<_Nm>::slice(std::size_t pos) const -> Bit <_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"); 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"); debug::assert(pos <= _Nm - _Len, "Bit::slice: pos should be less than _Nm - _Len");
return Bit<_Len>(this->_M_data >> pos); return Bit<_Len>(this->_M_data >> pos);
} }
} // namespace dark } // namespace dark

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <limits>
#include <cstdint>
#include <concepts> #include <concepts>
#include <cstdint>
#include <limits>
namespace dark { namespace dark {
@ -10,45 +10,45 @@ using max_ssize_t = std::int32_t;
static constexpr std::size_t kMaxLength = std::numeric_limits<max_size_t>::digits; static constexpr std::size_t kMaxLength = std::numeric_limits<max_size_t>::digits;
template <std::size_t _Len> template<std::size_t _Len>
consteval max_size_t make_mask() { consteval max_size_t make_mask() {
static_assert(_Len <= kMaxLength, "Mask length out of range"); static_assert(_Len <= kMaxLength, "Mask length out of range");
return _Len == kMaxLength ? ~max_size_t(0) : (max_size_t(1) << _Len) - 1; return _Len == kMaxLength ? ~max_size_t(0) : (max_size_t(1) << _Len) - 1;
} }
} // namespace dark } // namespace dark
namespace dark::concepts { namespace dark::concepts {
template <typename _Tp> template<typename _Tp>
using func_t = void(*)(_Tp); using func_t = void (*)(_Tp);
template <typename _From, typename _To> template<typename _From, typename _To>
concept implicit_convertible_to = requires(_From &a, func_t <_To> b) { concept implicit_convertible_to = requires(_From &a, func_t<_To> b) {
b(a); // Can implicitly convert b(a); // Can implicitly convert
}; };
template <typename _From, typename _To> template<typename _From, typename _To>
concept explicit_convertible_to = concept explicit_convertible_to =
!implicit_convertible_to <_From, _To> !implicit_convertible_to<_From, _To> && std::constructible_from<_To, _From>;
&& std::constructible_from <_To, _From>;
template <typename _Tp> template<typename _Tp>
concept has_length = requires { { +_Tp::_Bit_Len } -> std::same_as <std::size_t>; }; concept has_length = requires { { +_Tp::_Bit_Len } -> std::same_as <std::size_t>; };
template <typename _Tp> template<typename _Tp>
concept bit_type = has_length <_Tp> && explicit_convertible_to <_Tp, max_size_t>; concept bit_type = has_length<_Tp> && explicit_convertible_to<_Tp, max_size_t>;
template <typename _Tp> template<typename _Tp>
concept int_type = !has_length <_Tp> && implicit_convertible_to <_Tp, max_size_t>; concept int_type = !has_length<_Tp> && implicit_convertible_to<_Tp, max_size_t>;
template <typename _Lhs, typename _Rhs> template<typename _Lhs, typename _Rhs>
concept bit_match = concept bit_match =
(bit_type <_Lhs> && bit_type <_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len) (bit_type<_Lhs> && bit_type<_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len) // prevent format
|| (int_type <_Lhs> || int_type <_Rhs>); || (int_type<_Lhs> && bit_type<_Rhs>) //
|| (bit_type<_Lhs> && int_type<_Rhs>);
template <typename _Tp, std::size_t _Len> template<typename _Tp, std::size_t _Len>
concept bit_convertible = 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 } // namespace dark::concepts

63
include/cpu.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include "module.h"
#include <algorithm>
#include <memory>
#include <random>
#include <vector>
namespace dark {
class CPU {
private:
std::vector<std::unique_ptr<ModuleBase>> mod_owned;
std::vector<ModuleBase *> 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<typename _Tp>
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<ModuleBase> 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<ModuleBase *> 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

View File

@ -13,55 +13,59 @@ namespace dark::debug {
*/ */
[[noreturn]] inline void unreachable() { [[noreturn]] inline void unreachable() {
#if __cplusplus > 202002L #if __cplusplus > 202002L
std::unreachable(); std::unreachable();
#elif defined(_MSC_VER) && !defined(__clang__) // MSVC #elif defined(_MSC_VER) && !defined(__clang__) // MSVC
// Uses compiler specific extensions if possible. // Uses compiler specific extensions if possible.
// Even if no extension is used, undefined behavior is still raised by // Even if no extension is used, undefined behavior is still raised by
// an empty function body and the noreturn attribute. // an empty function body and the noreturn attribute.
__assume(false); __assume(false);
#else // GCC, Clang #else // GCC, Clang
__builtin_unreachable(); __builtin_unreachable();
#endif #endif
} }
template <typename _Tp, typename... _Args> template<typename _Tp, typename... _Args>
struct assert { struct assert {
#ifdef _DEBUG #ifdef _DEBUG
explicit assert(_Tp &&condition, _Args &&...args, explicit assert(_Tp &&condition, _Args &&...args,
std::source_location location = std::source_location::current()) { std::source_location location = std::source_location::current()) {
if (condition) return; if (condition) return;
std::cerr << "Assertion failed at: " std::cerr << "Assertion failed at: "
<< location.file_name() << ":" << location.line() << "\n"; << location.file_name() << ":" << location.line() << "\n";
if constexpr (sizeof...(args) != 0) { if constexpr (sizeof...(args) != 0) {
std::cerr << "Message: "; std::cerr << "Message: ";
((std::cerr << args), ...) << std::endl; ((std::cerr << args), ...) << std::endl;
} }
std::exit(EXIT_FAILURE); std::exit(EXIT_FAILURE);
} }
#else #else
explicit assert(_Tp &&, _Args &&...) {} explicit assert(_Tp &&, _Args &&...) {}
#endif #endif
}; };
template <typename _Tp, typename... _Args> template<typename _Tp, typename... _Args>
assert(_Tp &&, _Args &&...) -> assert<_Tp, _Args...>; assert(_Tp &&, _Args &&...) -> assert<_Tp, _Args...>;
template <typename _Tp, _Tp _Default> template<typename _Tp, _Tp _Default>
struct DebugValue { struct DebugValue {
#ifdef _DEBUG #ifdef _DEBUG
private: private:
_Tp _M_value = _Default; _Tp _M_value = _Default;
public:
auto get_value() const { return this->_M_value; } public:
auto set_value(_Tp value) { this->_M_value = value; } auto get_value() const { return this->_M_value; }
auto set_value(_Tp value) { this->_M_value = value; }
#else #else
public: public:
auto get_value() const { return _Default; } auto get_value() const { return _Default; }
auto set_value(_Tp) { /* do nothing */ } auto set_value(_Tp) { /* do nothing */ }
#endif #endif
public: public:
explicit operator _Tp() const { return this->get_value(); } explicit operator _Tp() const { return this->get_value(); }
DebugValue &operator=(_Tp value) { this->set_value(value); return *this; } DebugValue &operator=(_Tp value) {
this->set_value(value);
return *this;
}
}; };
} // namespace dark::debug } // namespace dark::debug

27
include/module.h Normal file
View File

@ -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<typename _Tinput, typename _Toutput, typename _Tprivate = details::empty_class>
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

View File

@ -3,127 +3,128 @@
namespace dark { namespace dark {
using dark::concepts::bit_match;
using dark::concepts::bit_type; using dark::concepts::bit_type;
using dark::concepts::int_type; using dark::concepts::int_type;
using dark::concepts::bit_match;
template <typename _Tp> template<typename _Tp>
constexpr auto cast(const _Tp& value) { constexpr auto cast(const _Tp &value) {
return static_cast <max_size_t> (value); return static_cast<max_size_t>(value);
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
consteval auto get_common_length() -> std::size_t { consteval auto get_common_length() -> std::size_t {
static_assert(bit_match <_Tp, _Up>); static_assert(bit_match<_Tp, _Up>);
if constexpr (bit_type <_Tp>) { if constexpr (bit_type<_Tp>) {
return _Tp::_Bit_Len; return _Tp::_Bit_Len;
} else { }
static_assert(bit_type <_Up>, "Invalid common length"); else {
return _Up::_Bit_Len; static_assert(bit_type<_Up>, "Invalid common length");
} return _Up::_Bit_Len;
}
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator + (const _Tp &lhs, const _Up &rhs) { constexpr auto operator+(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) + cast(rhs)); return Bit<_Len>(cast(lhs) + cast(rhs));
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator - (const _Tp &lhs, const _Up &rhs) { constexpr auto operator-(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) - cast(rhs)); return Bit<_Len>(cast(lhs) - cast(rhs));
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator * (const _Tp &lhs, const _Up &rhs) { constexpr auto operator*(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) * cast(rhs)); return Bit<_Len>(cast(lhs) * cast(rhs));
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator / (const _Tp &lhs, const _Up &rhs) { constexpr auto operator/(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) / cast(rhs)); return Bit<_Len>(cast(lhs) / cast(rhs));
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator & (const _Tp &lhs, const _Up &rhs) { constexpr auto operator&(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) & cast(rhs)); return Bit<_Len>(cast(lhs) & cast(rhs));
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator | (const _Tp &lhs, const _Up &rhs) { constexpr auto operator|(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) | cast(rhs)); return Bit<_Len>(cast(lhs) | cast(rhs));
} }
template <typename _Tp, typename _Up> template<typename _Tp, typename _Up>
requires bit_match <_Tp, _Up> requires bit_match<_Tp, _Up>
constexpr auto operator ^ (const _Tp &lhs, const _Up &rhs) { constexpr auto operator^(const _Tp &lhs, const _Up &rhs) {
constexpr auto _Len = get_common_length <_Tp, _Up>(); constexpr auto _Len = get_common_length<_Tp, _Up>();
return Bit <_Len> (cast(lhs) ^ cast(rhs)); return Bit<_Len>(cast(lhs) ^ cast(rhs));
} }
template <typename _Tp> template<typename _Tp>
concept int_or_bit = int_type <_Tp> || bit_type <_Tp>; concept int_or_bit = int_type<_Tp> || bit_type<_Tp>;
template <bit_type _Tp, int_or_bit _Up> template<bit_type _Tp, int_or_bit _Up>
constexpr auto operator << (const _Tp &lhs, const _Up &rhs) { constexpr auto operator<<(const _Tp &lhs, const _Up &rhs) {
return Bit <_Tp::_Bit_Len> (cast(lhs) << (cast(rhs) & kMaxLength)); return Bit<_Tp::_Bit_Len>(cast(lhs) << (cast(rhs) & kMaxLength));
} }
template <bit_type _Tp, int_or_bit _Up> template<bit_type _Tp, int_or_bit _Up>
constexpr auto operator >> (const _Tp &lhs, const _Up &rhs) { constexpr auto operator>>(const _Tp &lhs, const _Up &rhs) {
return Bit <_Tp::_Bit_Len> (cast(lhs) >> (cast(rhs) & kMaxLength)); return Bit<_Tp::_Bit_Len>(cast(lhs) >> (cast(rhs) & kMaxLength));
} }
template <bit_type _Tp> template<bit_type _Tp>
constexpr auto operator ~ (const _Tp &value) { constexpr auto operator~(const _Tp &value) {
return Bit <_Tp::_Bit_Len> (~cast(value)); return Bit<_Tp::_Bit_Len>(~cast(value));
} }
template <bit_type _Tp> template<bit_type _Tp>
constexpr auto operator ! (const _Tp &value) { constexpr auto operator!(const _Tp &value) {
return ~value; return ~value;
} }
template <bit_type _Tp> template<bit_type _Tp>
constexpr auto operator + (const _Tp &value) { constexpr auto operator+(const _Tp &value) {
return Bit <_Tp::_Bit_Len> (+cast(value)); return Bit<_Tp::_Bit_Len>(+cast(value));
} }
template <bit_type _Tp> template<bit_type _Tp>
constexpr auto operator - (const _Tp &value) { constexpr auto operator-(const _Tp &value) {
return Bit <_Tp::_Bit_Len> (-cast(value)); return Bit<_Tp::_Bit_Len>(-cast(value));
} }
template <int_or_bit _Tp, int_or_bit _Up> template<int_or_bit _Tp, int_or_bit _Up>
constexpr bool operator && (const _Tp &lhs, const _Up &rhs) { constexpr bool operator&&(const _Tp &lhs, const _Up &rhs) {
return cast(lhs) && cast(rhs); return cast(lhs) && cast(rhs);
} }
template <int_or_bit _Tp, int_or_bit _Up> template<int_or_bit _Tp, int_or_bit _Up>
constexpr bool operator || (const _Tp &lhs, const _Up &rhs) { constexpr bool operator||(const _Tp &lhs, const _Up &rhs) {
return cast(lhs) || cast(rhs); return cast(lhs) || cast(rhs);
} }
template <int_or_bit _Tp, int_or_bit _Up> template<int_or_bit _Tp, int_or_bit _Up>
constexpr bool operator == (const _Tp &lhs, const _Up &rhs) { constexpr bool operator==(const _Tp &lhs, const _Up &rhs) {
return cast(lhs) == cast(rhs); return cast(lhs) == cast(rhs);
} }
template <int_or_bit _Tp, int_or_bit _Up> template<int_or_bit _Tp, int_or_bit _Up>
constexpr auto operator <=> (const _Tp &lhs, const _Up &rhs) { constexpr auto operator<=>(const _Tp &lhs, const _Up &rhs) {
return cast(lhs) <=> cast(rhs); return cast(lhs) <=> cast(rhs);
} }

View File

@ -1,81 +1,104 @@
#pragma once #pragma once
#include <tuple>
#include <concepts> #include <concepts>
#include <tuple>
namespace dark::reflect { namespace dark::reflect {
/* A init helper to get the size of a struct. */ /* A init helper to get the size of a struct. */
struct init_helper { template <typename _Tp> operator _Tp(); }; struct init_helper {
template<typename _Tp>
operator _Tp();
};
/* A size helper to get the size of a struct. */ /* A size helper to get the size of a struct. */
template <typename _Tp> requires std::is_aggregate_v <_Tp> template<typename _Tp>
requires std::is_aggregate_v<_Tp>
inline consteval auto member_size_aux(auto &&...args) -> std::size_t { inline consteval auto member_size_aux(auto &&...args) -> std::size_t {
constexpr std::size_t size = sizeof...(args); constexpr std::size_t size = sizeof...(args);
constexpr std::size_t maximum = 114; constexpr std::size_t maximum = 114;
if constexpr (size > maximum) { if constexpr (size > maximum) {
static_assert (sizeof(_Tp) == 0, "The struct has too many members."); static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
} else if constexpr (!requires {_Tp { args... }; }) { }
return size - 1; else if constexpr (!requires { _Tp{args...}; }) {
} else { return size - 1;
return member_size_aux <_Tp> (args..., init_helper {}); }
} else {
return member_size_aux<_Tp>(args..., init_helper{});
}
} }
/* Return the member size for a aggregate type without base. */ /* Return the member size for a aggregate type without base. */
template <typename _Tp> requires std::is_aggregate_v <_Tp> template<typename _Tp>
inline consteval auto member_size(_Tp &) -> std::size_t { return member_size_aux <_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> template<typename _Tp>
inline consteval auto member_size() -> std::size_t { return member_size_aux <_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> template<typename _Tp>
requires std::is_aggregate_v<_Tp>
auto tuplify(_Tp &value) { auto tuplify(_Tp &value) {
constexpr auto size = member_size <_Tp> (); constexpr auto size = member_size<_Tp>();
if constexpr (size == 1) { if constexpr (size == 1) {
auto &[x0] = value; auto &[x0] = value;
return std::forward_as_tuple(x0); return std::forward_as_tuple(x0);
} else if constexpr (size == 2) { }
auto &[x0, x1] = value; else if constexpr (size == 2) {
return std::forward_as_tuple(x0, x1); auto &[x0, x1] = value;
} else if constexpr (size == 3) { return std::forward_as_tuple(x0, x1);
auto &[x0, x1, x2] = value; }
return std::forward_as_tuple(x0, x1, x2); else if constexpr (size == 3) {
} else if constexpr (size == 4) { auto &[x0, x1, x2] = value;
auto &[x0, x1, x2, x3] = value; return std::forward_as_tuple(x0, x1, x2);
return std::forward_as_tuple(x0, x1, x2, x3); }
} else if constexpr (size == 5) { else if constexpr (size == 4) {
auto &[x0, x1, x2, x3, x4] = value; auto &[x0, x1, x2, x3] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4); return std::forward_as_tuple(x0, x1, x2, x3);
} else if constexpr (size == 6) { }
auto &[x0, x1, x2, x3, x4, x5] = value; else if constexpr (size == 5) {
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5); auto &[x0, x1, x2, x3, x4] = value;
} else if constexpr (size == 7) { return std::forward_as_tuple(x0, x1, x2, x3, x4);
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 == 6) {
} else if constexpr (size == 8) { auto &[x0, x1, x2, x3, x4, x5] = value;
auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value; return std::forward_as_tuple(x0, x1, x2, x3, x4, x5);
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7); }
} else if constexpr (size == 9) { else if constexpr (size == 7) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8] = value; auto &[x0, x1, x2, x3, x4, x5, x6] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8); return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6);
} else if constexpr (size == 10) { }
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9] = value; else if constexpr (size == 8) {
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9); auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value;
} else if constexpr (size == 11) { return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7);
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 == 9) {
} else if constexpr (size == 12) { auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8] = value;
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);
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11); }
} else if constexpr (size == 13) { else if constexpr (size == 10) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12] = value; 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, x10, x11, x12); return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9);
} else if constexpr (size == 14) { }
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13] = value; else if constexpr (size == 11) {
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13); auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] = value;
} else { return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);
static_assert (sizeof(_Tp) == 0, "The struct has too many members."); }
} 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 } // namespace dark::reflect

View File

@ -1,48 +1,47 @@
#pragma once #pragma once
#include "debug.h"
#include "concept.h" #include "concept.h"
#include "debug.h"
namespace dark { namespace dark {
template <std::size_t _Len> template<std::size_t _Len>
struct Register { struct Register {
private: private:
static_assert(0 < _Len && _Len <= kMaxLength, static_assert(0 < _Len && _Len <= kMaxLength,
"Register: _Len must be in range [1, 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_old : _Len;
max_size_t _M_new : _Len; max_size_t _M_new : _Len;
[[no_unique_address]] [[no_unique_address]]
debug::DebugValue <bool, false> _M_assigned; debug::DebugValue<bool, false> _M_assigned;
void sync() { void sync() {
if (this->_M_assigned) { this->_M_assigned = false;
this->_M_assigned = false; this->_M_old = this->_M_new;
this->_M_old = this->_M_new; }
}
}
public: public:
static constexpr std::size_t _Bit_Len = _Len; 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(Register &&) = delete;
Register(const Register &) = delete; Register(const Register &) = delete;
Register &operator=(Register &&) = delete; Register &operator=(Register &&) = delete;
Register &operator=(const Register &rhs) = delete; Register &operator=(const Register &rhs) = delete;
template <concepts::bit_convertible <_Len> _Tp> template<concepts::bit_convertible<_Len> _Tp>
void operator <= (const _Tp &value) { void operator<=(const _Tp &value) {
debug::assert(!this->_M_assigned, "Register is double assigned in this cycle."); debug::assert(!this->_M_assigned, "Register is double assigned in this cycle.");
this->_M_assigned = true; this->_M_assigned = true;
this->_M_new = static_cast <max_size_t> (value); this->_M_new = static_cast<max_size_t>(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 } // namespace dark

View File

@ -5,56 +5,62 @@
namespace dark { namespace dark {
struct Visitor { struct Visitor {
template <typename _Tp> template<typename _Tp>
static constexpr bool is_syncable_v = static constexpr bool is_syncable_v =
requires(_Tp &val) { { val.sync() } -> std::same_as<void>; }; requires(_Tp &val) { { val.sync() } -> std::same_as<void>; };
template <typename _Tp> requires is_syncable_v<_Tp> template<typename _Tp>
static void sync(_Tp &val) { val.sync(); } requires is_syncable_v<_Tp>
static void sync(_Tp &val) { val.sync(); }
template <typename _Tp, typename _Base> template<typename _Tp, typename _Base>
static _Base &cast(_Tp &value) { return static_cast<_Base &>(value); } static _Base &cast(_Tp &value) { return static_cast<_Base &>(value); }
}; };
template <typename ..._Base> template<typename... _Base>
struct SyncTags {}; struct SyncTags {};
template <typename _Tp> template<typename _Tp>
static constexpr bool is_valid_tag_v = false; static constexpr bool is_valid_tag_v = false;
template <typename ..._Base> template<typename... _Base>
static constexpr bool is_valid_tag_v<SyncTags<_Base...>> = true; static constexpr bool is_valid_tag_v<SyncTags<_Base...>> = true;
template <typename _Tp> template<typename _Tp>
concept has_valid_tag = is_valid_tag_v<typename _Tp::Tags>; concept has_valid_tag = is_valid_tag_v<typename _Tp::Tags>;
template <typename _Tp> template<typename _Tp>
static constexpr bool is_std_array_v = std::is_array_v<_Tp>; static constexpr bool is_std_array_v = std::is_array_v<_Tp>;
template <typename _Tp, std::size_t _Nm> template<typename _Tp, std::size_t _Nm>
static constexpr bool is_std_array_v<std::array<_Tp, _Nm>> = true; static constexpr bool is_std_array_v<std::array<_Tp, _Nm>> = true;
template <typename _Tp> template<typename _Tp>
inline void sync_member(_Tp &value); inline void sync_member(_Tp &value);
template <typename _Tp, typename ..._Base> template<typename _Tp, typename... _Base>
inline void sync_by_tag(_Tp &value, SyncTags<_Base...>) { inline void sync_by_tag(_Tp &value, SyncTags<_Base...>) {
(sync_member(Visitor::cast<_Tp, _Base>(value)), ...); (sync_member(Visitor::cast<_Tp, _Base>(value)), ...);
} }
template <typename _Tp> template<typename _Tp>
inline void sync_member(_Tp &value) { inline void sync_member(_Tp &value) {
if constexpr (std::is_const_v <_Tp>) { if constexpr (std::is_const_v<_Tp>) {
/* Do nothing! Constant members need no synchronization! */ /* 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 (is_std_array_v<_Tp>) {
} else if constexpr (Visitor::is_syncable_v<_Tp>) { for (auto &member: value) sync_member(member);
Visitor::sync(value); }
} else if constexpr (has_valid_tag<_Tp>) { else if constexpr (Visitor::is_syncable_v<_Tp>) {
sync_by_tag(value, typename _Tp::Tags {}); Visitor::sync(value);
} else if constexpr (std::is_aggregate_v<_Tp>) { }
auto &&tuple = reflect::tuplify(value); else if constexpr (has_valid_tag<_Tp>) {
std::apply([](auto &...members) { (sync_member(members), ...); }, tuple); sync_by_tag(value, typename _Tp::Tags{});
} else { }
static_assert(sizeof(_Tp) == 0, "This type is not syncable."); 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

View File

@ -1,31 +1,33 @@
#pragma once #pragma once
#include "bit.h" #include "bit.h"
#include "bit_impl.h" #include "bit_impl.h"
#include "wire.h" #include "operator.h"
#include "register.h" #include "register.h"
#include "synchronize.h" #include "synchronize.h"
#include "operator.h" #include "wire.h"
#include "module.h"
#include "cpu.h"
using dark::Bit; using dark::Bit;
using dark::sign_extend; using dark::sign_extend;
using dark::zero_extend; using dark::zero_extend;
using dark::Wire;
using dark::Register; using dark::Register;
using dark::Wire;
using dark::SyncTags;
using dark::sync_member; using dark::sync_member;
using dark::SyncTags;
using dark::Visitor; using dark::Visitor;
using dark::max_size_t; using dark::max_size_t;
using dark::max_ssize_t; using dark::max_ssize_t;
template <dark::concepts::bit_type _Tp> template<dark::concepts::bit_type _Tp>
constexpr auto to_unsigned(const _Tp &x) { constexpr auto to_unsigned(const _Tp &x) {
return static_cast<dark::max_size_t>(x); return static_cast<dark::max_size_t>(x);
} }
template <dark::concepts::bit_type _Tp> template<dark::concepts::bit_type _Tp>
constexpr auto to_signed(const _Tp &x) { constexpr auto to_signed(const _Tp &x) {
return static_cast<dark::max_ssize_t>(to_unsigned(x)); return static_cast<dark::max_ssize_t>(sign_extend(x));
} }

View File

@ -1,113 +1,116 @@
#pragma once #pragma once
#include "debug.h"
#include "concept.h" #include "concept.h"
#include "debug.h"
#include <memory> #include <memory>
namespace dark { namespace dark {
namespace details { namespace details {
template <typename _Fn, std::size_t _Len> template<typename _Fn, std::size_t _Len>
concept WireFunction = concept WireFunction =
concepts::bit_convertible <std::decay_t <std::invoke_result_t <_Fn>>, _Len>; concepts::bit_convertible<std::decay_t<std::invoke_result_t<_Fn>>, _Len>;
struct FuncBase { struct FuncBase {
using _Ret_t = max_size_t; using _Ret_t = max_size_t;
using _Cpy_t = FuncBase *; using _Cpy_t = FuncBase *;
virtual _Ret_t call() const = 0; virtual _Ret_t call() const = 0;
virtual _Cpy_t copy() const = 0; virtual _Cpy_t copy() const = 0;
virtual ~FuncBase() = default; virtual ~FuncBase() = default;
}; };
template <std::size_t _Len, WireFunction <_Len> _Fn> template<std::size_t _Len, WireFunction<_Len> _Fn>
struct FuncImpl final : FuncBase { struct FuncImpl final : FuncBase {
_Fn _M_lambda; _Fn _M_lambda;
template <typename _Tp> template<typename _Tp>
FuncImpl(_Tp &&fn) : _M_lambda(std::forward <_Tp> (fn)) {} FuncImpl(_Tp &&fn) : _M_lambda(std::forward<_Tp>(fn)) {}
_Ret_t call() const override { return static_cast <_Ret_t> (this->_M_lambda()); } _Ret_t call() const override { return static_cast<_Ret_t>(this->_M_lambda()); }
_Cpy_t copy() const override { return new FuncImpl(*this); } _Cpy_t copy() const override { return new FuncImpl(*this); }
}; };
struct EmptyWire final : FuncBase { struct EmptyWire final : FuncBase {
_Ret_t call() const override { _Ret_t call() const override {
debug::assert(false, "Empty wire is called."); debug::assert(false, "Empty wire is called.");
debug::unreachable(); debug::unreachable();
} }
_Cpy_t copy() const override { return new EmptyWire; } _Cpy_t copy() const override { return new EmptyWire; }
}; };
} // namespace details } // namespace details
template <std::size_t _Len> template<std::size_t _Len>
struct Wire { struct Wire {
private: private:
static_assert(0 < _Len && _Len <= kMaxLength, static_assert(0 < _Len && _Len <= kMaxLength,
"Wire: _Len must be in range [1, kMaxLength]."); "Wire: _Len must be in range [1, kMaxLength].");
friend class Visitor; friend class Visitor;
using _Manage_t = std::unique_ptr <details::FuncBase>; using _Manage_t = std::unique_ptr<details::FuncBase>;
_Manage_t _M_func; _Manage_t _M_func;
mutable max_size_t _M_cache : _Len; mutable max_size_t _M_cache : _Len;
mutable bool _M_holds; mutable bool _M_holds;
[[no_unique_address]] [[no_unique_address]]
debug::DebugValue <bool, false> _M_assigned; debug::DebugValue<bool, false> _M_assigned;
void sync() { this->_M_holds = false; } private:
void sync() { this->_M_holds = false; }
template <details::WireFunction <_Len> _Fn> template<details::WireFunction<_Len> _Fn>
static auto _M_new_func(_Fn &&fn) { static auto _M_new_func(_Fn &&fn) {
using _Decay_t = std::decay_t <_Fn>; using _Decay_t = std::decay_t<_Fn>;
return new details::FuncImpl <_Len, _Decay_t> {std::forward <_Fn>(fn)}; return new details::FuncImpl<_Len, _Decay_t>{std::forward<_Fn>(fn)};
} }
void _M_checked_assign() { void _M_checked_assign() {
debug::assert(!this->_M_assigned, "Wire is assigned twice."); debug::assert(!this->_M_assigned, "Wire is assigned twice.");
this->_M_assigned = true; this->_M_assigned = true;
} }
public: public:
static constexpr std::size_t _Bit_Len = _Len; static constexpr std::size_t _Bit_Len = _Len;
Wire() : Wire() : _M_func(new details::EmptyWire),
_M_func(new details::EmptyWire), _M_cache(), _M_holds(), _M_assigned() {}
_M_cache(), _M_holds(), _M_assigned() {}
explicit operator max_size_t() const { explicit operator max_size_t() const {
if (this->_M_holds == false) { if (this->_M_holds == false) {
this->_M_holds = true; this->_M_holds = true;
this->_M_cache = this->_M_func->call(); this->_M_cache = this->_M_func->call();
} }
return this->_M_cache; return this->_M_cache;
} }
Wire(Wire &&) = delete; Wire(Wire &&) = delete;
Wire(const Wire &) = delete; Wire(const Wire &) = delete;
Wire &operator=(Wire &&) = delete; Wire &operator=(Wire &&) = delete;
Wire &operator=(const Wire &rhs) = delete; Wire &operator=(const Wire &rhs) = delete;
template <details::WireFunction <_Len> _Fn> template<details::WireFunction<_Len> _Fn>
Wire(_Fn &&fn) : Wire(_Fn &&fn) : _M_func(_M_new_func(std::forward<_Fn>(fn))),
_M_func(_M_new_func(std::forward <_Fn> (fn))), _M_cache(), _M_holds(), _M_assigned() {}
_M_cache(), _M_holds(), _M_assigned() {}
template <details::WireFunction <_Len> _Fn> template<details::WireFunction<_Len> _Fn>
Wire &operator=(_Fn &&fn) { Wire &operator=(_Fn &&fn) {
return this->assign(std::forward <_Fn> (fn)), *this; return this->assign(std::forward<_Fn>(fn)), *this;
} }
template <details::WireFunction <_Len> _Fn> template<details::WireFunction<_Len> _Fn>
void assign(_Fn &&fn) { void assign(_Fn &&fn) {
this->_M_checked_assign(); this->_M_checked_assign();
this->_M_func.reset(_M_new_func(std::forward <_Fn> (fn))); this->_M_func.reset(_M_new_func(std::forward<_Fn>(fn)));
this->sync(); this->sync();
} }
explicit operator bool() const {
return static_cast<max_size_t>(*this);
}
}; };