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)
|
92
demo/alu.cpp
92
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,7 +20,6 @@ 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;
|
||||||
@ -36,13 +33,9 @@ struct AluOutput {
|
|||||||
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() {
|
|
||||||
if (reset) {
|
|
||||||
done <= 0;
|
|
||||||
} else if(ready && issue) {
|
|
||||||
switch (static_cast<Opcode>(static_cast<unsigned>(opcode))) {
|
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;
|
||||||
@ -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 函数中检查某个寄存器的值,这些大家可以自行实现。
|
||||||
|
|
||||||
|
为了保证正确性,在最终测试中,应当保证模块执行的顺序与运行结果无关。
|
71
docs/help.md
71
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
|
||||||
@ -81,13 +82,13 @@ Wire <4> wire2 = [®]() -> auto & { return reg; };
|
|||||||
wire = []() { return 0b11010; };
|
wire = []() { return 0b11010; };
|
||||||
|
|
||||||
// Ill formed! Wire cannot 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,9 +1,9 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@ constexpr auto int_concat(max_size_t arg, auto ...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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +28,9 @@ constexpr auto sign_extend(const _Tp & 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,5 +86,4 @@ constexpr auto Bit<_Nm>::slice(std::size_t pos) const -> Bit <_Len> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // 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 {
|
||||||
|
|
||||||
@ -30,8 +30,7 @@ concept implicit_convertible_to = requires(_From &a, func_t <_To> b) {
|
|||||||
|
|
||||||
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>; };
|
||||||
@ -44,8 +43,9 @@ 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 =
|
||||||
|
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
|
@ -51,6 +51,7 @@ 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; }
|
||||||
@ -61,7 +62,10 @@ struct DebugValue {
|
|||||||
#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,9 +3,9 @@
|
|||||||
|
|
||||||
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) {
|
||||||
@ -17,7 +17,8 @@ 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 {
|
}
|
||||||
|
else {
|
||||||
static_assert(bit_type<_Up>, "Invalid common length");
|
static_assert(bit_type<_Up>, "Invalid common length");
|
||||||
return _Up::_Bit_Len;
|
return _Up::_Bit_Len;
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,102 @@
|
|||||||
#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 {
|
}
|
||||||
|
else {
|
||||||
return member_size_aux<_Tp>(args..., init_helper{});
|
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>
|
||||||
|
requires std::is_aggregate_v<_Tp>
|
||||||
inline consteval auto member_size(_Tp &) -> std::size_t { return member_size_aux<_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>
|
||||||
|
requires std::is_aggregate_v<_Tp>
|
||||||
inline consteval auto member_size() -> std::size_t { return member_size_aux<_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 {
|
}
|
||||||
|
else {
|
||||||
static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
|
static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "debug.h"
|
|
||||||
#include "concept.h"
|
#include "concept.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
namespace dark {
|
namespace dark {
|
||||||
|
|
||||||
@ -19,11 +19,9 @@ struct Register {
|
|||||||
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;
|
||||||
@ -43,6 +41,7 @@ struct Register {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -9,7 +9,8 @@ struct Visitor {
|
|||||||
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>
|
||||||
@ -43,18 +44,23 @@ 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>) {
|
}
|
||||||
|
else if constexpr (is_std_array_v<_Tp>) {
|
||||||
for (auto &member: value) sync_member(member);
|
for (auto &member: value) sync_member(member);
|
||||||
} else if constexpr (Visitor::is_syncable_v<_Tp>) {
|
}
|
||||||
|
else if constexpr (Visitor::is_syncable_v<_Tp>) {
|
||||||
Visitor::sync(value);
|
Visitor::sync(value);
|
||||||
} else if constexpr (has_valid_tag<_Tp>) {
|
}
|
||||||
|
else if constexpr (has_valid_tag<_Tp>) {
|
||||||
sync_by_tag(value, typename _Tp::Tags{});
|
sync_by_tag(value, typename _Tp::Tags{});
|
||||||
} else if constexpr (std::is_aggregate_v<_Tp>) {
|
}
|
||||||
|
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,20 +1,22 @@
|
|||||||
#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;
|
||||||
@ -27,5 +29,5 @@ constexpr auto to_unsigned(const _Tp &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,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "debug.h"
|
|
||||||
#include "concept.h"
|
#include "concept.h"
|
||||||
|
#include "debug.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace dark {
|
namespace dark {
|
||||||
@ -59,6 +59,7 @@ struct Wire {
|
|||||||
[[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>
|
||||||
@ -75,8 +76,7 @@ struct Wire {
|
|||||||
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 {
|
||||||
@ -93,8 +93,7 @@ struct Wire {
|
|||||||
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>
|
||||||
@ -108,6 +107,10 @@ struct Wire {
|
|||||||
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