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>
|
||||
|
||||
bool reset;
|
||||
bool ready;
|
||||
#include <unordered_map>
|
||||
|
||||
// RISC-V
|
||||
enum class Opcode : dark::max_size_t {
|
||||
@ -22,28 +20,23 @@ enum class Opcode : dark::max_size_t {
|
||||
SNEQ
|
||||
};
|
||||
|
||||
|
||||
// Normally, only wire can be used in the input.
|
||||
struct AluInput {
|
||||
Wire <8> opcode;
|
||||
Wire <1> issue;
|
||||
Wire <32> rs1;
|
||||
Wire <32> rs2;
|
||||
Wire<8> opcode;
|
||||
Wire<1> issue;
|
||||
Wire<32> rs1;
|
||||
Wire<32> rs2;
|
||||
};
|
||||
|
||||
struct AluOutput {
|
||||
Register <32> out;
|
||||
Register <1> done;
|
||||
Register<32> out;
|
||||
Register<1> done;
|
||||
};
|
||||
|
||||
struct AluModule : AluInput, AluOutput {
|
||||
using Tags = SyncTags<AluInput, AluOutput>;
|
||||
|
||||
void work() {
|
||||
if (reset) {
|
||||
done <= 0;
|
||||
} else if(ready && issue) {
|
||||
switch (static_cast <Opcode> (static_cast <unsigned> (opcode))) {
|
||||
struct AluModule : dark::Module<AluInput, AluOutput> {
|
||||
void work() override {
|
||||
if (issue) {
|
||||
switch (static_cast<Opcode>(static_cast<unsigned>(opcode))) {
|
||||
using enum Opcode;
|
||||
case ADD: 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");
|
||||
}
|
||||
done <= 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
done <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
signed main() {
|
||||
AluModule alu;
|
||||
|
||||
int main() {
|
||||
std::string opstring;
|
||||
|
||||
max_size_t opcode;
|
||||
@ -79,53 +70,42 @@ signed main() {
|
||||
max_size_t rs1;
|
||||
max_size_t rs2;
|
||||
|
||||
ready = 1;
|
||||
|
||||
dark::CPU cpu;
|
||||
AluModule alu;
|
||||
alu.opcode = [&]() { return opcode; };
|
||||
alu.issue = [&]() { return issue; };
|
||||
alu.rs1 = [&]() { return rs1; };
|
||||
alu.rs2 = [&]() { return rs2; };
|
||||
cpu.add_module(&alu);
|
||||
|
||||
std::unordered_map<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) {
|
||||
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 {
|
||||
if (cmd2op.find(opstring) == cmd2op.end()) {
|
||||
std::cout << "Invalid opcode" << std::endl;
|
||||
issue = 0;
|
||||
}
|
||||
|
||||
alu.work();
|
||||
|
||||
std::cout << to_unsigned(alu.out) << std::endl;
|
||||
sync_member(alu);
|
||||
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
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
|
||||
behave like normal integers, except that we have a similar
|
||||
bit-width matching check as the verilog integers.
|
||||
(e.g. 4-bit register can only be assigned from a 4-bit value)
|
||||
When using this library, it's important to note that all the value types behave like regular integers, with the exception that we have a similar bit-width matching check as with Verilog integers.
|
||||
(e.g. a 4-bit register can only be assigned from a 4-bit value)
|
||||
|
||||
Also, you should use the recommended way to perform the auto-synchronization,
|
||||
which can (hope so) save you from writing a lot of duplicated code.
|
||||
Additionally, it is recommended to use the provided method for auto-synchronization, which can potentially save you from writing a lot of duplicated code.
|
||||
|
||||
## Requirements
|
||||
|
||||
`g++-12` or later. `-std=c++20` or `-std=c++2b`.
|
||||
You will need `g++-12` or later, with the flags `-std=c++20`.
|
||||
|
||||
e.g. `g++ -std=c++2b ...`
|
||||
Example: `g++ -std=c++20 ...`
|
||||
|
||||
## Include the library
|
||||
Your code may still run on `g++-11` or earlier, but we do not guarantee it.
|
||||
|
||||
This is a header-only library, which means you just need to include all your required headers in your project.
|
||||
## Including the Library
|
||||
|
||||
We strongly recommend you to include `include/tools.h` to simply include all the headers.
|
||||
This is a header-only library, which means you simply need to include all the required headers in your project.
|
||||
|
||||
We strongly recommend including `include/tools.h` to easily include all the headers.
|
||||
|
||||
```cpp
|
||||
#include "include/tools.h"
|
||||
```
|
||||
|
||||
## Debug mode
|
||||
## Debug Mode
|
||||
|
||||
We provide a debug mode, which will perform more checks in the code. To enable that,
|
||||
just define the macro `_DEBUG` before including the headers.
|
||||
You may also pass `-D _DEBUG` to the compiling command to define the macro.
|
||||
We provide a debug mode, which performs additional checks in the code. To enable this, simply define the macro `_DEBUG` before including the headers.
|
||||
You can also pass `-D _DEBUG` to the compiler to define the macro, or define it directly in your code.
|
||||
|
||||
```cpp
|
||||
#define _DEBUG
|
||||
```
|
||||
|
||||
## Value types
|
||||
We **strongly recommend** enabling the debug mode when developing your project.
|
||||
|
||||
You may at first treat all these types as the verilog integers.
|
||||
You may assume all the types below support basic arithmetic operations,
|
||||
and will **clip** the value just as the verilog integers operations.
|
||||
Example: `g++ -std=c++20 -D _DEBUG ...`
|
||||
|
||||
## Value Types
|
||||
|
||||
Initially, you can treat all these types as Verilog integers.
|
||||
You can assume that all the types below support basic arithmetic operations and will **clip** the value just like Verilog integer operations.
|
||||
|
||||
### Register
|
||||
|
||||
Registers are just like the registers in the verilog.
|
||||
Registers are similar to those in Verilog.
|
||||
|
||||
To simulate the registers, a `Register` is only allowed to be assigned once in a cycle.
|
||||
To simulate registers, a `Register` is only allowed to be assigned once in a cycle.
|
||||
|
||||
```cpp
|
||||
// Declare a 32-bit register
|
||||
// The maximum bit-width depends on the max_size_t
|
||||
// Currently, the max_size_t is std::uint32_t
|
||||
Register<32> reg;
|
||||
reg <= reg + 1; // OK, allow to assign from some value with the same bit-width
|
||||
reg <= reg + 1; // OK, allows assignment from a value with the same bit-width
|
||||
Register<16> reg2;
|
||||
reg <= reg2 * reg2; // Compile error, the bit-width is different (32 vs 16)
|
||||
```
|
||||
|
||||
### Wire
|
||||
|
||||
Wires are also similar to the wires in the verilog.
|
||||
Wires are also similar to those in Verilog.
|
||||
|
||||
It should be assigned exactly once before reading.
|
||||
It can accept a function-like input (function pointers/lambdas) to extract the value.
|
||||
They should be assigned exactly once before reading.
|
||||
They can accept a function-like input (function pointers/lambdas) to extract the value.
|
||||
|
||||
```cpp
|
||||
// Declare a 4-bit wire
|
||||
@ -80,14 +81,14 @@ Wire <4> wire2 = [®]() -> auto & { return reg; };
|
||||
// Ill formed! The wire is assigned twice
|
||||
wire = []() { return 0b11010; };
|
||||
|
||||
// Ill formed! Wire can not accept a value
|
||||
// with different bit-width
|
||||
// Ill formed! Wire cannot accept a value
|
||||
// with a different bit-width
|
||||
Wire <5> wire3 = [&]() -> auto & { return reg + 4; };
|
||||
```
|
||||
|
||||
### Bit
|
||||
|
||||
Bit is an intermediate type, which can be used to represent an integer with some bit_width.
|
||||
Bit is an intermediate type, which can be used to represent an integer with a specific bit width.
|
||||
|
||||
```cpp
|
||||
Bit <5> b = 0b111111; // Clipped to 0b11111
|
||||
@ -100,20 +101,18 @@ Bit <4> d = b.slice <4> (1); // Copy 4 bits from bit 1 (bit 4, 3, 2, 1) to d
|
||||
Bit <1> e = d[0]; // Get the 0-th bit of d
|
||||
|
||||
Bit f = { b + 3, c, d }; // Concatenate b + 3, c, d from high to low
|
||||
|
||||
```
|
||||
|
||||
## Synchronization
|
||||
|
||||
We support a feature of auto synchronization, which means that you can
|
||||
easily synchronize all the members of a class by simply calling the `sync_member` function.
|
||||
We support a feature of auto synchronization, which means that you can easily synchronize all the members of a class by simply calling the `sync_member` function.
|
||||
|
||||
We support 4 types of synchronization:
|
||||
|
||||
1. Register / Wire type synchronization.
|
||||
2. An array (only std::array is supported) of synchronizable objects.
|
||||
3. A class which contains only synchronizable objects, and satisfies std::is_aggregate.
|
||||
4. A class which has some basis which are synchronizable objects, and has a special tag.
|
||||
3. A class which contains only synchronizable objects and satisfies std::is_aggregate.
|
||||
4. A class which has some basis that are synchronizable objects, and has a special tag.
|
||||
|
||||
We will show some examples of 3 and 4.
|
||||
|
||||
@ -155,10 +154,10 @@ void demo() {
|
||||
}
|
||||
```
|
||||
|
||||
## Common mistakes
|
||||
## Common Mistakes
|
||||
|
||||
Turn to the [mistake](mistake.md) page to see some common mistakes.
|
||||
Refer to the [mistake](mistake.md) page to see some common mistakes.
|
||||
|
||||
## Examples
|
||||
|
||||
See demo folder for more examples.
|
||||
See the demo folder for more examples.
|
||||
|
@ -1,9 +1,9 @@
|
||||
# Some common mistakes
|
||||
# Some Common Mistakes
|
||||
|
||||
## Bit-width mismatch
|
||||
## Bit-width Mismatch
|
||||
|
||||
That is, the bit-width of the LHS and RHS of an assignment operation are different.
|
||||
For example, the following code will result in compile error:
|
||||
This occurs when the bit-width of the left-hand side (LHS) and right-hand side (RHS) of an assignment operation are different.
|
||||
For example, the following code will result in a compile error:
|
||||
|
||||
```cpp
|
||||
Register <8> r1;
|
||||
@ -11,32 +11,28 @@ Register <16> r2;
|
||||
r1 <= r2; // Error: bit-width mismatch
|
||||
```
|
||||
|
||||
## Register/Wire passed by value
|
||||
## Register/Wire Passed by Value
|
||||
|
||||
Register/Wire can be only passed by reference. We forbid
|
||||
the copy/move constructor for Register/Wire to avoid misuse.
|
||||
Register/Wire can only be passed by reference. We forbid the copy/move constructor for Register/Wire to prevent misuse.
|
||||
|
||||
This may cause some error in the lambda function of a wire.
|
||||
This may cause errors in the lambda function of a wire.
|
||||
|
||||
```cpp
|
||||
Register <8> r1;
|
||||
Wire <8> w1 = [&]() { return r1; };
|
||||
```
|
||||
|
||||
To fix this issue, you may return by reference,
|
||||
or use + operator to convert the value to bit type.
|
||||
To fix this issue, you may return by reference or use the + operator to convert the value to a bit type.
|
||||
|
||||
```cpp
|
||||
Register <8> r1;
|
||||
Wire <8> w1 = [&]() -> auto & { return r1; };
|
||||
Wire <8> w2 = [&]() { return +r1; };
|
||||
Wire <8> w2 = [&]() { return +r1; }; // +r1 will return Bit<8>b
|
||||
```
|
||||
|
||||
## C-array as member variable
|
||||
## C-array as Member Variable
|
||||
|
||||
We do not support C-array as member variable for synchronization.
|
||||
Our C++ static reflection library do not support parsing
|
||||
C-array as member variable currently.
|
||||
We do not support C-arrays as member variables for synchronization. Our C++ static reflection library does not currently support parsing C-arrays as member variables.
|
||||
|
||||
Always use `std::array` instead.
|
||||
|
||||
@ -47,6 +43,6 @@ struct NeedToSync {
|
||||
};
|
||||
```
|
||||
|
||||
## Some others
|
||||
## Others
|
||||
|
||||
If you encounter some other issues, please feel free to open an issue.
|
||||
If you encounter other issues, please feel free to open an issue.
|
||||
|
@ -1,50 +1,50 @@
|
||||
#pragma once
|
||||
#include "concept.h"
|
||||
#include "debug.h"
|
||||
#include <version>
|
||||
#include <climits>
|
||||
#include <concepts>
|
||||
#include <version>
|
||||
|
||||
namespace dark {
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template<std::size_t _Nm>
|
||||
struct Bit {
|
||||
private:
|
||||
private:
|
||||
static_assert(0 < _Nm && _Nm <= kMaxLength,
|
||||
"Bit: _Nm out of range. Should be in [1, kMaxLength]");
|
||||
|
||||
max_size_t _M_data : _Nm; // Real storage
|
||||
|
||||
template <std::size_t _Hi, std::size_t _Lo>
|
||||
template<std::size_t _Hi, std::size_t _Lo>
|
||||
static constexpr void _M_range_check();
|
||||
|
||||
public:
|
||||
public:
|
||||
static constexpr std::size_t _Bit_Len = _Nm;
|
||||
|
||||
constexpr Bit(max_size_t data = 0) : _M_data(data) {}
|
||||
|
||||
constexpr explicit operator max_size_t() const { return this->_M_data; }
|
||||
|
||||
template <concepts::bit_type ..._Tp>
|
||||
requires ((_Tp::_Bit_Len + ...) == _Nm)
|
||||
template<concepts::bit_type... _Tp>
|
||||
requires((_Tp::_Bit_Len + ...) == _Nm)
|
||||
constexpr Bit(const _Tp &...args);
|
||||
|
||||
template <concepts::bit_convertible <_Nm> _Tp>
|
||||
template<concepts::bit_convertible<_Nm> _Tp>
|
||||
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);
|
||||
|
||||
template <std::size_t _Hi, std::size_t _Lo = _Hi>
|
||||
constexpr auto range() const -> Bit <_Hi - _Lo + 1>;
|
||||
template<std::size_t _Hi, std::size_t _Lo = _Hi>
|
||||
constexpr auto range() const -> Bit<_Hi - _Lo + 1>;
|
||||
|
||||
template <std::size_t _Len = 1>
|
||||
constexpr auto slice(std::size_t pos) const -> Bit <_Len>;
|
||||
template<std::size_t _Len = 1>
|
||||
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 + ...)>;
|
||||
|
||||
} // namespace dark
|
||||
|
@ -3,60 +3,64 @@
|
||||
|
||||
namespace dark {
|
||||
|
||||
template <int _First>
|
||||
template<int _First>
|
||||
constexpr auto int_concat(max_size_t arg) { return arg; }
|
||||
|
||||
template <int _First, int ..._Lens>
|
||||
constexpr auto int_concat(max_size_t arg, auto ...args) {
|
||||
template<int _First, int... _Lens>
|
||||
constexpr auto int_concat(max_size_t arg, auto... args) {
|
||||
return (arg << (_Lens + ...)) | int_concat<_Lens...>(args...);
|
||||
}
|
||||
|
||||
template <std::size_t _Old, std::size_t _New = kMaxLength>
|
||||
template<std::size_t _Old, std::size_t _New = kMaxLength>
|
||||
constexpr auto sign_extend(max_size_t val) {
|
||||
static_assert(_Old < _New, "sign_extend: _Old should be less than _New");
|
||||
struct { max_ssize_t _M_data : _Old; } tmp;
|
||||
struct {
|
||||
max_ssize_t _M_data : _Old;
|
||||
} tmp;
|
||||
return Bit<_New>(tmp._M_data = val);
|
||||
}
|
||||
|
||||
template <std::size_t _New = kMaxLength, concepts::bit_type _Tp>
|
||||
constexpr auto sign_extend(const _Tp & val) {
|
||||
template<std::size_t _New = kMaxLength, concepts::bit_type _Tp>
|
||||
constexpr auto sign_extend(const _Tp &val) {
|
||||
return sign_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val));
|
||||
}
|
||||
|
||||
template <std::size_t _Old, std::size_t _New = kMaxLength>
|
||||
template<std::size_t _Old, std::size_t _New = kMaxLength>
|
||||
constexpr auto zero_extend(max_size_t val) {
|
||||
static_assert(_Old < _New, "zero_extend: _Old should be less than _New");
|
||||
struct { max_size_t _M_data : _Old; } tmp;
|
||||
struct {
|
||||
max_size_t _M_data : _Old;
|
||||
} tmp;
|
||||
return Bit<_New>(tmp._M_data = val);
|
||||
}
|
||||
|
||||
template <std::size_t _New = kMaxLength, concepts::bit_type _Tp>
|
||||
template<std::size_t _New = kMaxLength, concepts::bit_type _Tp>
|
||||
constexpr auto zero_extend(const _Tp &val) {
|
||||
return zero_extend<_Tp::_Bit_Len, _New>(static_cast<max_size_t>(val));
|
||||
}
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template <concepts::bit_type ..._Tp>
|
||||
requires ((_Tp::_Bit_Len + ...) == _Nm)
|
||||
template<std::size_t _Nm>
|
||||
template<concepts::bit_type... _Tp>
|
||||
requires((_Tp::_Bit_Len + ...) == _Nm)
|
||||
constexpr Bit<_Nm>::Bit(const _Tp &...args)
|
||||
: _M_data(int_concat<_Tp::_Bit_Len...>(static_cast<max_size_t>(args)...)) {}
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template <concepts::bit_convertible <_Nm> _Tp>
|
||||
template<std::size_t _Nm>
|
||||
template<concepts::bit_convertible<_Nm> _Tp>
|
||||
constexpr Bit<_Nm> &Bit<_Nm>::operator=(const _Tp &val) {
|
||||
this->_M_data = static_cast<max_size_t>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template <std::size_t _Hi, std::size_t _Lo>
|
||||
template<std::size_t _Nm>
|
||||
template<std::size_t _Hi, std::size_t _Lo>
|
||||
constexpr void Bit<_Nm>::_M_range_check() {
|
||||
static_assert(_Lo <= _Hi, "Bit::range_check: _Lo should be no greater than _Hi");
|
||||
static_assert(_Hi < _Nm, "Bit::range_check: _Hi should be less than _Nm");
|
||||
}
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template <std::size_t _Hi, std::size_t _Lo, concepts::bit_convertible <_Nm> _Tp>
|
||||
template<std::size_t _Nm>
|
||||
template<std::size_t _Hi, std::size_t _Lo, concepts::bit_convertible<_Nm> _Tp>
|
||||
constexpr void Bit<_Nm>::set(const _Tp &val) {
|
||||
this->_M_range_check<_Hi, _Lo>();
|
||||
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);
|
||||
}
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template <std::size_t _Hi, std::size_t _Lo>
|
||||
constexpr auto Bit<_Nm>::range() const -> Bit <_Hi - _Lo + 1> {
|
||||
template<std::size_t _Nm>
|
||||
template<std::size_t _Hi, std::size_t _Lo>
|
||||
constexpr auto Bit<_Nm>::range() const -> Bit<_Hi - _Lo + 1> {
|
||||
this->_M_range_check<_Hi, _Lo>();
|
||||
constexpr auto _Length = _Hi - _Lo + 1;
|
||||
return Bit<_Length>(this->_M_data >> _Lo);
|
||||
}
|
||||
|
||||
template <std::size_t _Nm>
|
||||
template <std::size_t _Len>
|
||||
constexpr auto Bit<_Nm>::slice(std::size_t pos) const -> Bit <_Len> {
|
||||
template<std::size_t _Nm>
|
||||
template<std::size_t _Len>
|
||||
constexpr auto Bit<_Nm>::slice(std::size_t pos) const -> Bit<_Len> {
|
||||
static_assert(_Len <= _Nm, "Bit::slice: _Len should be no greater than _Nm");
|
||||
debug::assert(pos <= _Nm - _Len, "Bit::slice: pos should be less than _Nm - _Len");
|
||||
return Bit<_Len>(this->_M_data >> pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace dark
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
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;
|
||||
|
||||
template <std::size_t _Len>
|
||||
template<std::size_t _Len>
|
||||
consteval max_size_t make_mask() {
|
||||
static_assert(_Len <= kMaxLength, "Mask length out of range");
|
||||
return _Len == kMaxLength ? ~max_size_t(0) : (max_size_t(1) << _Len) - 1;
|
||||
@ -20,35 +20,35 @@ consteval max_size_t make_mask() {
|
||||
|
||||
namespace dark::concepts {
|
||||
|
||||
template <typename _Tp>
|
||||
using func_t = void(*)(_Tp);
|
||||
template<typename _Tp>
|
||||
using func_t = void (*)(_Tp);
|
||||
|
||||
template <typename _From, typename _To>
|
||||
concept implicit_convertible_to = requires(_From &a, func_t <_To> b) {
|
||||
template<typename _From, typename _To>
|
||||
concept implicit_convertible_to = requires(_From &a, func_t<_To> b) {
|
||||
b(a); // Can implicitly convert
|
||||
};
|
||||
|
||||
template <typename _From, typename _To>
|
||||
template<typename _From, typename _To>
|
||||
concept explicit_convertible_to =
|
||||
!implicit_convertible_to <_From, _To>
|
||||
&& std::constructible_from <_To, _From>;
|
||||
!implicit_convertible_to<_From, _To> && std::constructible_from<_To, _From>;
|
||||
|
||||
template <typename _Tp>
|
||||
template<typename _Tp>
|
||||
concept has_length = requires { { +_Tp::_Bit_Len } -> std::same_as <std::size_t>; };
|
||||
|
||||
template <typename _Tp>
|
||||
concept bit_type = has_length <_Tp> && explicit_convertible_to <_Tp, max_size_t>;
|
||||
template<typename _Tp>
|
||||
concept bit_type = has_length<_Tp> && explicit_convertible_to<_Tp, max_size_t>;
|
||||
|
||||
template <typename _Tp>
|
||||
concept int_type = !has_length <_Tp> && implicit_convertible_to <_Tp, max_size_t>;
|
||||
template<typename _Tp>
|
||||
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 =
|
||||
(bit_type <_Lhs> && bit_type <_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len)
|
||||
|| (int_type <_Lhs> || int_type <_Rhs>);
|
||||
(bit_type<_Lhs> && bit_type<_Rhs> && _Lhs::_Bit_Len == _Rhs::_Bit_Len) // prevent format
|
||||
|| (int_type<_Lhs> && bit_type<_Rhs>) //
|
||||
|| (bit_type<_Lhs> && int_type<_Rhs>);
|
||||
|
||||
template <typename _Tp, std::size_t _Len>
|
||||
template<typename _Tp, std::size_t _Len>
|
||||
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
|
||||
|
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
|
||||
}
|
||||
|
||||
template <typename _Tp, typename... _Args>
|
||||
template<typename _Tp, typename... _Args>
|
||||
struct assert {
|
||||
#ifdef _DEBUG
|
||||
explicit assert(_Tp &&condition, _Args &&...args,
|
||||
@ -43,25 +43,29 @@ struct assert {
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename _Tp, typename... _Args>
|
||||
template<typename _Tp, typename... _Args>
|
||||
assert(_Tp &&, _Args &&...) -> assert<_Tp, _Args...>;
|
||||
|
||||
template <typename _Tp, _Tp _Default>
|
||||
template<typename _Tp, _Tp _Default>
|
||||
struct DebugValue {
|
||||
#ifdef _DEBUG
|
||||
private:
|
||||
private:
|
||||
_Tp _M_value = _Default;
|
||||
public:
|
||||
|
||||
public:
|
||||
auto get_value() const { return this->_M_value; }
|
||||
auto set_value(_Tp value) { this->_M_value = value; }
|
||||
#else
|
||||
public:
|
||||
public:
|
||||
auto get_value() const { return _Default; }
|
||||
auto set_value(_Tp) { /* do nothing */ }
|
||||
#endif
|
||||
public:
|
||||
public:
|
||||
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
|
||||
|
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 {
|
||||
|
||||
using dark::concepts::bit_match;
|
||||
using dark::concepts::bit_type;
|
||||
using dark::concepts::int_type;
|
||||
using dark::concepts::bit_match;
|
||||
|
||||
template <typename _Tp>
|
||||
constexpr auto cast(const _Tp& value) {
|
||||
return static_cast <max_size_t> (value);
|
||||
template<typename _Tp>
|
||||
constexpr auto cast(const _Tp &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 {
|
||||
static_assert(bit_match <_Tp, _Up>);
|
||||
if constexpr (bit_type <_Tp>) {
|
||||
static_assert(bit_match<_Tp, _Up>);
|
||||
if constexpr (bit_type<_Tp>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator + (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) + cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator+(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) + cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator - (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) - cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator-(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) - cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator * (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) * cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator*(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) * cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator / (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) / cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator/(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) / cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator & (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) & cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator&(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) & cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator | (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) | cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator|(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) | cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Up>
|
||||
requires bit_match <_Tp, _Up>
|
||||
constexpr auto operator ^ (const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length <_Tp, _Up>();
|
||||
return Bit <_Len> (cast(lhs) ^ cast(rhs));
|
||||
template<typename _Tp, typename _Up>
|
||||
requires bit_match<_Tp, _Up>
|
||||
constexpr auto operator^(const _Tp &lhs, const _Up &rhs) {
|
||||
constexpr auto _Len = get_common_length<_Tp, _Up>();
|
||||
return Bit<_Len>(cast(lhs) ^ cast(rhs));
|
||||
}
|
||||
|
||||
template <typename _Tp>
|
||||
concept int_or_bit = int_type <_Tp> || bit_type <_Tp>;
|
||||
template<typename _Tp>
|
||||
concept int_or_bit = int_type<_Tp> || bit_type<_Tp>;
|
||||
|
||||
template <bit_type _Tp, int_or_bit _Up>
|
||||
constexpr auto operator << (const _Tp &lhs, const _Up &rhs) {
|
||||
return Bit <_Tp::_Bit_Len> (cast(lhs) << (cast(rhs) & kMaxLength));
|
||||
template<bit_type _Tp, int_or_bit _Up>
|
||||
constexpr auto operator<<(const _Tp &lhs, const _Up &rhs) {
|
||||
return Bit<_Tp::_Bit_Len>(cast(lhs) << (cast(rhs) & kMaxLength));
|
||||
}
|
||||
|
||||
template <bit_type _Tp, int_or_bit _Up>
|
||||
constexpr auto operator >> (const _Tp &lhs, const _Up &rhs) {
|
||||
return Bit <_Tp::_Bit_Len> (cast(lhs) >> (cast(rhs) & kMaxLength));
|
||||
template<bit_type _Tp, int_or_bit _Up>
|
||||
constexpr auto operator>>(const _Tp &lhs, const _Up &rhs) {
|
||||
return Bit<_Tp::_Bit_Len>(cast(lhs) >> (cast(rhs) & kMaxLength));
|
||||
}
|
||||
|
||||
template <bit_type _Tp>
|
||||
constexpr auto operator ~ (const _Tp &value) {
|
||||
return Bit <_Tp::_Bit_Len> (~cast(value));
|
||||
template<bit_type _Tp>
|
||||
constexpr auto operator~(const _Tp &value) {
|
||||
return Bit<_Tp::_Bit_Len>(~cast(value));
|
||||
}
|
||||
|
||||
template <bit_type _Tp>
|
||||
constexpr auto operator ! (const _Tp &value) {
|
||||
template<bit_type _Tp>
|
||||
constexpr auto operator!(const _Tp &value) {
|
||||
return ~value;
|
||||
}
|
||||
|
||||
template <bit_type _Tp>
|
||||
constexpr auto operator + (const _Tp &value) {
|
||||
return Bit <_Tp::_Bit_Len> (+cast(value));
|
||||
template<bit_type _Tp>
|
||||
constexpr auto operator+(const _Tp &value) {
|
||||
return Bit<_Tp::_Bit_Len>(+cast(value));
|
||||
}
|
||||
|
||||
template <bit_type _Tp>
|
||||
constexpr auto operator - (const _Tp &value) {
|
||||
return Bit <_Tp::_Bit_Len> (-cast(value));
|
||||
template<bit_type _Tp>
|
||||
constexpr auto operator-(const _Tp &value) {
|
||||
return Bit<_Tp::_Bit_Len>(-cast(value));
|
||||
}
|
||||
|
||||
template <int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr bool operator && (const _Tp &lhs, const _Up &rhs) {
|
||||
template<int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr bool operator&&(const _Tp &lhs, const _Up &rhs) {
|
||||
return cast(lhs) && cast(rhs);
|
||||
}
|
||||
|
||||
template <int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr bool operator || (const _Tp &lhs, const _Up &rhs) {
|
||||
template<int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr bool operator||(const _Tp &lhs, const _Up &rhs) {
|
||||
return cast(lhs) || cast(rhs);
|
||||
}
|
||||
|
||||
template <int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr bool operator == (const _Tp &lhs, const _Up &rhs) {
|
||||
template<int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr bool operator==(const _Tp &lhs, const _Up &rhs) {
|
||||
return cast(lhs) == cast(rhs);
|
||||
}
|
||||
|
||||
template <int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr auto operator <=> (const _Tp &lhs, const _Up &rhs) {
|
||||
template<int_or_bit _Tp, int_or_bit _Up>
|
||||
constexpr auto operator<=>(const _Tp &lhs, const _Up &rhs) {
|
||||
return cast(lhs) <=> cast(rhs);
|
||||
}
|
||||
|
||||
|
@ -1,80 +1,103 @@
|
||||
#pragma once
|
||||
#include <tuple>
|
||||
#include <concepts>
|
||||
#include <tuple>
|
||||
|
||||
namespace dark::reflect {
|
||||
|
||||
/* 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. */
|
||||
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 {
|
||||
constexpr std::size_t size = sizeof...(args);
|
||||
constexpr std::size_t maximum = 114;
|
||||
if constexpr (size > maximum) {
|
||||
static_assert (sizeof(_Tp) == 0, "The struct has too many members.");
|
||||
} else if constexpr (!requires {_Tp { args... }; }) {
|
||||
static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
|
||||
}
|
||||
else if constexpr (!requires { _Tp{args...}; }) {
|
||||
return size - 1;
|
||||
} else {
|
||||
return member_size_aux <_Tp> (args..., init_helper {});
|
||||
}
|
||||
else {
|
||||
return member_size_aux<_Tp>(args..., init_helper{});
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the member size for a aggregate type without base. */
|
||||
template <typename _Tp> requires std::is_aggregate_v <_Tp>
|
||||
inline consteval auto member_size(_Tp &) -> std::size_t { return member_size_aux <_Tp> (); }
|
||||
template<typename _Tp>
|
||||
requires std::is_aggregate_v<_Tp>
|
||||
inline consteval auto member_size(_Tp &) -> std::size_t { return member_size_aux<_Tp>(); }
|
||||
|
||||
template <typename _Tp> requires std::is_aggregate_v <_Tp>
|
||||
inline consteval auto member_size() -> std::size_t { return member_size_aux <_Tp> (); }
|
||||
template<typename _Tp>
|
||||
requires std::is_aggregate_v<_Tp>
|
||||
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) {
|
||||
constexpr auto size = member_size <_Tp> ();
|
||||
constexpr auto size = member_size<_Tp>();
|
||||
if constexpr (size == 1) {
|
||||
auto &[x0] = value;
|
||||
return std::forward_as_tuple(x0);
|
||||
} else if constexpr (size == 2) {
|
||||
}
|
||||
else if constexpr (size == 2) {
|
||||
auto &[x0, x1] = value;
|
||||
return std::forward_as_tuple(x0, x1);
|
||||
} else if constexpr (size == 3) {
|
||||
}
|
||||
else if constexpr (size == 3) {
|
||||
auto &[x0, x1, x2] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2);
|
||||
} else if constexpr (size == 4) {
|
||||
}
|
||||
else if constexpr (size == 4) {
|
||||
auto &[x0, x1, x2, x3] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3);
|
||||
} else if constexpr (size == 5) {
|
||||
}
|
||||
else if constexpr (size == 5) {
|
||||
auto &[x0, x1, x2, x3, x4] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4);
|
||||
} else if constexpr (size == 6) {
|
||||
}
|
||||
else if constexpr (size == 6) {
|
||||
auto &[x0, x1, x2, x3, x4, x5] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5);
|
||||
} else if constexpr (size == 7) {
|
||||
}
|
||||
else if constexpr (size == 7) {
|
||||
auto &[x0, x1, x2, x3, x4, x5, x6] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6);
|
||||
} else if constexpr (size == 8) {
|
||||
}
|
||||
else if constexpr (size == 8) {
|
||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7);
|
||||
} else if constexpr (size == 9) {
|
||||
}
|
||||
else if constexpr (size == 9) {
|
||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8);
|
||||
} else if constexpr (size == 10) {
|
||||
}
|
||||
else if constexpr (size == 10) {
|
||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9);
|
||||
} else if constexpr (size == 11) {
|
||||
}
|
||||
else if constexpr (size == 11) {
|
||||
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] = value;
|
||||
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);
|
||||
} else if constexpr (size == 12) {
|
||||
}
|
||||
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) {
|
||||
}
|
||||
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) {
|
||||
}
|
||||
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.");
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
#include "debug.h"
|
||||
#include "concept.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace dark {
|
||||
|
||||
template <std::size_t _Len>
|
||||
template<std::size_t _Len>
|
||||
struct Register {
|
||||
private:
|
||||
private:
|
||||
static_assert(0 < _Len && _Len <= kMaxLength,
|
||||
"Register: _Len must be in range [1, kMaxLength].");
|
||||
|
||||
@ -16,16 +16,14 @@ struct Register {
|
||||
max_size_t _M_new : _Len;
|
||||
|
||||
[[no_unique_address]]
|
||||
debug::DebugValue <bool, false> _M_assigned;
|
||||
debug::DebugValue<bool, false> _M_assigned;
|
||||
|
||||
void sync() {
|
||||
if (this->_M_assigned) {
|
||||
this->_M_assigned = false;
|
||||
this->_M_old = this->_M_new;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
static constexpr std::size_t _Bit_Len = _Len;
|
||||
|
||||
Register() : _M_old(), _M_new(), _M_assigned() {}
|
||||
@ -35,14 +33,15 @@ struct Register {
|
||||
Register &operator=(Register &&) = delete;
|
||||
Register &operator=(const Register &rhs) = delete;
|
||||
|
||||
template <concepts::bit_convertible <_Len> _Tp>
|
||||
void operator <= (const _Tp &value) {
|
||||
template<concepts::bit_convertible<_Len> _Tp>
|
||||
void operator<=(const _Tp &value) {
|
||||
debug::assert(!this->_M_assigned, "Register is double assigned in this cycle.");
|
||||
this->_M_assigned = true;
|
||||
this->_M_new = static_cast <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 bool() const { return this->_M_old; }
|
||||
};
|
||||
|
||||
} // namespace dark
|
||||
|
@ -5,56 +5,62 @@
|
||||
namespace dark {
|
||||
|
||||
struct Visitor {
|
||||
template <typename _Tp>
|
||||
template<typename _Tp>
|
||||
static constexpr bool is_syncable_v =
|
||||
requires(_Tp &val) { { val.sync() } -> std::same_as<void>; };
|
||||
|
||||
template <typename _Tp> requires is_syncable_v<_Tp>
|
||||
template<typename _Tp>
|
||||
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); }
|
||||
};
|
||||
|
||||
template <typename ..._Base>
|
||||
template<typename... _Base>
|
||||
struct SyncTags {};
|
||||
|
||||
template <typename _Tp>
|
||||
template<typename _Tp>
|
||||
static constexpr bool is_valid_tag_v = false;
|
||||
template <typename ..._Base>
|
||||
template<typename... _Base>
|
||||
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>;
|
||||
|
||||
template <typename _Tp>
|
||||
template<typename _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;
|
||||
|
||||
template <typename _Tp>
|
||||
template<typename _Tp>
|
||||
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...>) {
|
||||
(sync_member(Visitor::cast<_Tp, _Base>(value)), ...);
|
||||
}
|
||||
|
||||
template <typename _Tp>
|
||||
template<typename _Tp>
|
||||
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! */
|
||||
} else if constexpr (is_std_array_v<_Tp>) {
|
||||
for (auto &member : value) sync_member(member);
|
||||
} else if constexpr (Visitor::is_syncable_v<_Tp>) {
|
||||
}
|
||||
else if constexpr (is_std_array_v<_Tp>) {
|
||||
for (auto &member: value) sync_member(member);
|
||||
}
|
||||
else if constexpr (Visitor::is_syncable_v<_Tp>) {
|
||||
Visitor::sync(value);
|
||||
} else if constexpr (has_valid_tag<_Tp>) {
|
||||
sync_by_tag(value, typename _Tp::Tags {});
|
||||
} else if constexpr (std::is_aggregate_v<_Tp>) {
|
||||
}
|
||||
else if constexpr (has_valid_tag<_Tp>) {
|
||||
sync_by_tag(value, typename _Tp::Tags{});
|
||||
}
|
||||
else if constexpr (std::is_aggregate_v<_Tp>) {
|
||||
auto &&tuple = reflect::tuplify(value);
|
||||
std::apply([](auto &...members) { (sync_member(members), ...); }, tuple);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(_Tp) == 0, "This type is not syncable.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dark::hardware
|
||||
} // namespace dark
|
||||
|
@ -1,31 +1,33 @@
|
||||
#pragma once
|
||||
#include "bit.h"
|
||||
#include "bit_impl.h"
|
||||
#include "wire.h"
|
||||
#include "operator.h"
|
||||
#include "register.h"
|
||||
#include "synchronize.h"
|
||||
#include "operator.h"
|
||||
#include "wire.h"
|
||||
#include "module.h"
|
||||
#include "cpu.h"
|
||||
|
||||
using dark::Bit;
|
||||
using dark::sign_extend;
|
||||
using dark::zero_extend;
|
||||
|
||||
using dark::Wire;
|
||||
using dark::Register;
|
||||
using dark::Wire;
|
||||
|
||||
using dark::SyncTags;
|
||||
using dark::sync_member;
|
||||
using dark::SyncTags;
|
||||
using dark::Visitor;
|
||||
|
||||
using dark::max_size_t;
|
||||
using dark::max_ssize_t;
|
||||
|
||||
template <dark::concepts::bit_type _Tp>
|
||||
template<dark::concepts::bit_type _Tp>
|
||||
constexpr auto to_unsigned(const _Tp &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) {
|
||||
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
|
||||
#include "debug.h"
|
||||
#include "concept.h"
|
||||
#include "debug.h"
|
||||
#include <memory>
|
||||
|
||||
namespace dark {
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename _Fn, std::size_t _Len>
|
||||
concept WireFunction =
|
||||
concepts::bit_convertible <std::decay_t <std::invoke_result_t <_Fn>>, _Len>;
|
||||
template<typename _Fn, std::size_t _Len>
|
||||
concept WireFunction =
|
||||
concepts::bit_convertible<std::decay_t<std::invoke_result_t<_Fn>>, _Len>;
|
||||
|
||||
struct FuncBase {
|
||||
struct FuncBase {
|
||||
using _Ret_t = max_size_t;
|
||||
using _Cpy_t = FuncBase *;
|
||||
virtual _Ret_t call() const = 0;
|
||||
virtual _Cpy_t copy() const = 0;
|
||||
virtual ~FuncBase() = default;
|
||||
};
|
||||
};
|
||||
|
||||
template <std::size_t _Len, WireFunction <_Len> _Fn>
|
||||
struct FuncImpl final : FuncBase {
|
||||
template<std::size_t _Len, WireFunction<_Len> _Fn>
|
||||
struct FuncImpl final : FuncBase {
|
||||
_Fn _M_lambda;
|
||||
|
||||
template <typename _Tp>
|
||||
FuncImpl(_Tp &&fn) : _M_lambda(std::forward <_Tp> (fn)) {}
|
||||
template<typename _Tp>
|
||||
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); }
|
||||
};
|
||||
};
|
||||
|
||||
struct EmptyWire final : FuncBase {
|
||||
struct EmptyWire final : FuncBase {
|
||||
_Ret_t call() const override {
|
||||
debug::assert(false, "Empty wire is called.");
|
||||
debug::unreachable();
|
||||
}
|
||||
_Cpy_t copy() const override { return new EmptyWire; }
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
|
||||
template <std::size_t _Len>
|
||||
template<std::size_t _Len>
|
||||
struct Wire {
|
||||
private:
|
||||
private:
|
||||
static_assert(0 < _Len && _Len <= kMaxLength,
|
||||
"Wire: _Len must be in range [1, kMaxLength].");
|
||||
|
||||
friend class Visitor;
|
||||
|
||||
using _Manage_t = std::unique_ptr <details::FuncBase>;
|
||||
using _Manage_t = std::unique_ptr<details::FuncBase>;
|
||||
|
||||
_Manage_t _M_func;
|
||||
|
||||
@ -57,14 +57,15 @@ struct Wire {
|
||||
mutable bool _M_holds;
|
||||
|
||||
[[no_unique_address]]
|
||||
debug::DebugValue <bool, false> _M_assigned;
|
||||
debug::DebugValue<bool, false> _M_assigned;
|
||||
|
||||
private:
|
||||
void sync() { this->_M_holds = false; }
|
||||
|
||||
template <details::WireFunction <_Len> _Fn>
|
||||
template<details::WireFunction<_Len> _Fn>
|
||||
static auto _M_new_func(_Fn &&fn) {
|
||||
using _Decay_t = std::decay_t <_Fn>;
|
||||
return new details::FuncImpl <_Len, _Decay_t> {std::forward <_Fn>(fn)};
|
||||
using _Decay_t = std::decay_t<_Fn>;
|
||||
return new details::FuncImpl<_Len, _Decay_t>{std::forward<_Fn>(fn)};
|
||||
}
|
||||
|
||||
void _M_checked_assign() {
|
||||
@ -72,11 +73,10 @@ struct Wire {
|
||||
this->_M_assigned = true;
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
static constexpr std::size_t _Bit_Len = _Len;
|
||||
|
||||
Wire() :
|
||||
_M_func(new details::EmptyWire),
|
||||
Wire() : _M_func(new details::EmptyWire),
|
||||
_M_cache(), _M_holds(), _M_assigned() {}
|
||||
|
||||
explicit operator max_size_t() const {
|
||||
@ -92,22 +92,25 @@ struct Wire {
|
||||
Wire &operator=(Wire &&) = delete;
|
||||
Wire &operator=(const Wire &rhs) = delete;
|
||||
|
||||
template <details::WireFunction <_Len> _Fn>
|
||||
Wire(_Fn &&fn) :
|
||||
_M_func(_M_new_func(std::forward <_Fn> (fn))),
|
||||
template<details::WireFunction<_Len> _Fn>
|
||||
Wire(_Fn &&fn) : _M_func(_M_new_func(std::forward<_Fn>(fn))),
|
||||
_M_cache(), _M_holds(), _M_assigned() {}
|
||||
|
||||
template <details::WireFunction <_Len> _Fn>
|
||||
template<details::WireFunction<_Len> _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) {
|
||||
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();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return static_cast<max_size_t>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user