Merge branch 'DarkSharpness:main' into main
This commit is contained in:
12
CMakeLists.txt
Normal file
12
CMakeLists.txt
Normal 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)
|
106
demo/alu.cpp
106
demo/alu.cpp
@ -1,8 +1,6 @@
|
|||||||
#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 {
|
||||||
@ -22,28 +20,23 @@ enum class Opcode : dark::max_size_t {
|
|||||||
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) {
|
|
||||||
done <= 0;
|
|
||||||
} else if(ready && issue) {
|
|
||||||
switch (static_cast <Opcode> (static_cast <unsigned> (opcode))) {
|
|
||||||
using enum Opcode;
|
using enum Opcode;
|
||||||
case ADD: out <= (rs1 + rs2); break;
|
case ADD: out <= (rs1 + rs2); break;
|
||||||
case SUB: out <= (rs1 - rs2); break;
|
case SUB: out <= (rs1 - rs2); break;
|
||||||
@ -62,16 +55,14 @@ struct AluModule : AluInput, AluOutput {
|
|||||||
default: dark::debug::assert(false, "Invalid opcode");
|
default: dark::debug::assert(false, "Invalid opcode");
|
||||||
}
|
}
|
||||||
done <= 1;
|
done <= 1;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
done <= 0;
|
done <= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
signed main() {
|
int main() {
|
||||||
AluModule alu;
|
|
||||||
|
|
||||||
std::string opstring;
|
std::string opstring;
|
||||||
|
|
||||||
max_size_t opcode;
|
max_size_t opcode;
|
||||||
@ -79,53 +70,42 @@ signed main() {
|
|||||||
max_size_t rs1;
|
max_size_t rs1;
|
||||||
max_size_t rs2;
|
max_size_t rs2;
|
||||||
|
|
||||||
ready = 1;
|
dark::CPU cpu;
|
||||||
|
AluModule alu;
|
||||||
alu.opcode = [&]() { return opcode; };
|
alu.opcode = [&]() { return opcode; };
|
||||||
alu.issue = [&]() { return issue; };
|
alu.issue = [&]() { return issue; };
|
||||||
alu.rs1 = [&]() { return rs1; };
|
alu.rs1 = [&]() { return rs1; };
|
||||||
alu.rs2 = [&]() { return rs2; };
|
alu.rs2 = [&]() { return rs2; };
|
||||||
|
cpu.add_module(&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) {
|
while (std::cin >> opstring) {
|
||||||
issue = 1;
|
if (cmd2op.find(opstring) == cmd2op.end()) {
|
||||||
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;
|
std::cout << "Invalid opcode" << std::endl;
|
||||||
issue = 0;
|
issue = 0;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
alu.work();
|
issue = 1;
|
||||||
|
std::cin >> rs1 >> rs2;
|
||||||
std::cout << to_unsigned(alu.out) << std::endl;
|
|
||||||
sync_member(alu);
|
|
||||||
}
|
}
|
||||||
|
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
63
docs/frame.md
Normal 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 函数中检查某个寄存器的值,这些大家可以自行实现。
|
||||||
|
|
||||||
|
为了保证正确性,在最终测试中,应当保证模块执行的顺序与运行结果无关。
|
73
docs/help.md
73
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
|
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 = [®]() -> 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.
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -3,60 +3,64 @@
|
|||||||
|
|
||||||
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 {
|
||||||
|
max_ssize_t _M_data : _Old;
|
||||||
|
} tmp;
|
||||||
return Bit<_New>(tmp._M_data = val);
|
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 {
|
||||||
|
max_size_t _M_data : _Old;
|
||||||
|
} tmp;
|
||||||
return Bit<_New>(tmp._M_data = val);
|
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);
|
||||||
@ -65,22 +69,21 @@ constexpr void Bit<_Nm>::set(const _Tp &val) {
|
|||||||
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
|
||||||
|
@ -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,7 +10,7 @@ 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;
|
||||||
@ -20,35 +20,35 @@ consteval max_size_t make_mask() {
|
|||||||
|
|
||||||
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
63
include/cpu.h
Normal 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
|
@ -24,7 +24,7 @@ namespace dark::debug {
|
|||||||
#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,
|
||||||
@ -43,25 +43,29 @@ struct assert {
|
|||||||
#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:
|
|
||||||
|
public:
|
||||||
auto get_value() const { return this->_M_value; }
|
auto get_value() const { return this->_M_value; }
|
||||||
auto set_value(_Tp value) { this->_M_value = 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
27
include/module.h
Normal 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
|
@ -3,126 +3,127 @@
|
|||||||
|
|
||||||
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 {
|
||||||
|
static_assert(bit_type<_Up>, "Invalid common length");
|
||||||
return _Up::_Bit_Len;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,80 +1,103 @@
|
|||||||
#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... }; }) {
|
}
|
||||||
|
else if constexpr (!requires { _Tp{args...}; }) {
|
||||||
return size - 1;
|
return size - 1;
|
||||||
} else {
|
}
|
||||||
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) {
|
}
|
||||||
|
else if constexpr (size == 2) {
|
||||||
auto &[x0, x1] = value;
|
auto &[x0, x1] = value;
|
||||||
return std::forward_as_tuple(x0, x1);
|
return std::forward_as_tuple(x0, x1);
|
||||||
} else if constexpr (size == 3) {
|
}
|
||||||
|
else if constexpr (size == 3) {
|
||||||
auto &[x0, x1, x2] = value;
|
auto &[x0, x1, x2] = value;
|
||||||
return std::forward_as_tuple(x0, x1, x2);
|
return std::forward_as_tuple(x0, x1, x2);
|
||||||
} else if constexpr (size == 4) {
|
}
|
||||||
|
else if constexpr (size == 4) {
|
||||||
auto &[x0, x1, x2, x3] = value;
|
auto &[x0, x1, x2, x3] = value;
|
||||||
return std::forward_as_tuple(x0, x1, x2, x3);
|
return std::forward_as_tuple(x0, x1, x2, x3);
|
||||||
} else if constexpr (size == 5) {
|
}
|
||||||
|
else if constexpr (size == 5) {
|
||||||
auto &[x0, x1, x2, x3, x4] = value;
|
auto &[x0, x1, x2, x3, x4] = value;
|
||||||
return std::forward_as_tuple(x0, x1, x2, x3, x4);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4);
|
||||||
} else if constexpr (size == 6) {
|
}
|
||||||
|
else if constexpr (size == 6) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5] = value;
|
auto &[x0, x1, x2, x3, x4, x5] = value;
|
||||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5);
|
||||||
} else if constexpr (size == 7) {
|
}
|
||||||
|
else if constexpr (size == 7) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6] = value;
|
auto &[x0, x1, x2, x3, x4, x5, x6] = value;
|
||||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6);
|
||||||
} else if constexpr (size == 8) {
|
}
|
||||||
|
else if constexpr (size == 8) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value;
|
auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value;
|
||||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7);
|
||||||
} else if constexpr (size == 9) {
|
}
|
||||||
|
else if constexpr (size == 9) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8] = value;
|
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);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8);
|
||||||
} else if constexpr (size == 10) {
|
}
|
||||||
|
else if constexpr (size == 10) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9] = 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);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9);
|
||||||
} else if constexpr (size == 11) {
|
}
|
||||||
|
else if constexpr (size == 11) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] = value;
|
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);
|
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);
|
||||||
} else if constexpr (size == 12) {
|
}
|
||||||
|
else if constexpr (size == 12) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11] = 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, x9, x10, x11);
|
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 == 13) {
|
||||||
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, x10, x11, x12] = 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, x10, x11, x12);
|
||||||
} else if constexpr (size == 14) {
|
}
|
||||||
|
else if constexpr (size == 14) {
|
||||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13] = value;
|
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);
|
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.");
|
else {
|
||||||
|
static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#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].");
|
||||||
|
|
||||||
@ -16,16 +16,14 @@ struct Register {
|
|||||||
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() {}
|
||||||
@ -35,14 +33,15 @@ struct Register {
|
|||||||
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
|
||||||
|
@ -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>
|
||||||
|
requires is_syncable_v<_Tp>
|
||||||
static void sync(_Tp &val) { val.sync(); }
|
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);
|
||||||
|
}
|
||||||
|
else if constexpr (Visitor::is_syncable_v<_Tp>) {
|
||||||
Visitor::sync(value);
|
Visitor::sync(value);
|
||||||
} else if constexpr (has_valid_tag<_Tp>) {
|
}
|
||||||
sync_by_tag(value, typename _Tp::Tags {});
|
else if constexpr (has_valid_tag<_Tp>) {
|
||||||
} else if constexpr (std::is_aggregate_v<_Tp>) {
|
sync_by_tag(value, typename _Tp::Tags{});
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_aggregate_v<_Tp>) {
|
||||||
auto &&tuple = reflect::tuplify(value);
|
auto &&tuple = reflect::tuplify(value);
|
||||||
std::apply([](auto &...members) { (sync_member(members), ...); }, tuple);
|
std::apply([](auto &...members) { (sync_member(members), ...); }, tuple);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
static_assert(sizeof(_Tp) == 0, "This type is not syncable.");
|
static_assert(sizeof(_Tp) == 0, "This type is not syncable.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dark::hardware
|
} // namespace dark
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
#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;
|
||||||
|
|
||||||
@ -57,14 +57,15 @@ struct Wire {
|
|||||||
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;
|
||||||
|
|
||||||
|
private:
|
||||||
void sync() { this->_M_holds = false; }
|
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() {
|
||||||
@ -72,11 +73,10 @@ struct Wire {
|
|||||||
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 {
|
||||||
@ -92,22 +92,25 @@ struct Wire {
|
|||||||
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user