Merge branch 'DarkSharpness:main' into main

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

12
CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.20)
project(simulator)
file(GLOB_RECURSE sources "src/*.cpp")
set(CMAKE_CXX_STANDARD 20)
include_directories(include)
add_executable(simulator ${sources})
add_executable(alu demo/alu.cpp)

View File

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

63
docs/frame.md Normal file
View File

@ -0,0 +1,63 @@
# 整体框架
cpu应当区分为很多个模块每个模块应当交由 `CPU` 类进行管理。
在配置好模块后,通过 `add_module` 函数将模块添加到 `CPU` 类中。
这里,可以选择将 unique_ptr 传入,也可以选择将裸指针传入;对于后者,你应该自行确保内存访问安全。
在定义每个模块时我们认为模块中需要存储的值由三部分构成Input, Output 和内部隐藏的数据。
因此,对于每个模块,你需要定义两个或三个**简单结构体**以表示这些数据,然后通过多继承的方法来定义模块。
为了便于进行每个周期的寄存器更新,我们定义了模板类 `Module`, 你只需要继承 `Module` 类并传入你定义的结构体即可。
例如,定义模块 A 如下:
```cpp
struct AInput {
Wire<1> ready;
Wire<32> data;
};
struct AOutput {
Wire<1> valid;
Wire<32> data;
};
struct AInner {
Register<32> reg;
};
struct A : Module<AInput, AOutput, AInner> {
// TODO
};
```
在定义了数据之后,你需要实现模块内部的逻辑。你需要实现 `work` 虚函数,该函数将在每个周期被调用。
```cpp
struct A : Module<AInput, AOutput, AInner> {
void work() override {
// TODO
}
};
```
在实现了模块之后,你可以将模块添加到 `CPU` 类中。
```cpp
CPU cpu;
std::unique_ptr<A> a = std::make_unique<A>();
// TODO: 为 a 连线
cpu.add_module(a);
```
你也可以
```cpp
CPU cpu;
A a; // a's lifetime should be at least as long as cpu's
cpu.add_module(&a);
```
最后,调用 `cpu.run()` 即可开始模拟。
为了能够在结束时退出,你可以通过在 `work` 函数中抛出异常来结束模拟。
当然,你也可以自行修改 work 函数的签名,返回一些信息并在 `run` 函数中判断是否结束模拟。
或者,你可以在 run 函数中检查某个寄存器的值,这些大家可以自行实现。
为了保证正确性,在最终测试中,应当保证模块执行的顺序与运行结果无关。

View File

@ -1,67 +1,68 @@
# How to use
# How to Use
You need to keep in mind that all the value types just
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 = [&reg]() -> 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.

View File

@ -1,9 +1,9 @@
# Some common mistakes
# Some Common Mistakes
## Bit-width mismatch
## Bit-width Mismatch
That is, the bit-width of the LHS and RHS of an assignment operation are different.
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.

View File

@ -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:
static_assert(0 < _Nm && _Nm <= kMaxLength,
"Bit: _Nm out of range. Should be in [1, kMaxLength]");
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
max_size_t _M_data : _Nm; // Real storage
template <std::size_t _Hi, std::size_t _Lo>
static constexpr void _M_range_check();
template<std::size_t _Hi, std::size_t _Lo>
static constexpr void _M_range_check();
public:
static constexpr std::size_t _Bit_Len = _Nm;
public:
static constexpr std::size_t _Bit_Len = _Nm;
constexpr Bit(max_size_t data = 0) : _M_data(data) {}
constexpr Bit(max_size_t data = 0) : _M_data(data) {}
constexpr explicit operator max_size_t() const { return this->_M_data; }
constexpr explicit operator max_size_t() const { return this->_M_data; }
template <concepts::bit_type ..._Tp>
requires ((_Tp::_Bit_Len + ...) == _Nm)
constexpr Bit(const _Tp &...args);
template<concepts::bit_type... _Tp>
requires((_Tp::_Bit_Len + ...) == _Nm)
constexpr Bit(const _Tp &...args);
template <concepts::bit_convertible <_Nm> _Tp>
constexpr Bit &operator=(const _Tp &val);
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>
constexpr void set(const _Tp &val);
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

View File

@ -3,84 +3,87 @@
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) {
return (arg << (_Lens + ...)) | int_concat<_Lens...>(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;
return Bit<_New>(tmp._M_data = val);
static_assert(_Old < _New, "sign_extend: _Old should be less than _New");
struct {
max_ssize_t _M_data : _Old;
} tmp;
return Bit<_New>(tmp._M_data = val);
}
template <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 _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;
return Bit<_New>(tmp._M_data = val);
static_assert(_Old < _New, "zero_extend: _Old should be less than _New");
struct {
max_size_t _M_data : _Old;
} tmp;
return Bit<_New>(tmp._M_data = val);
}
template <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));
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)...)) {}
: _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;
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");
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);
constexpr auto _Length = _Hi - _Lo + 1;
auto mask = make_mask<_Length> << _Lo;
this->_M_data = (this->_M_data & ~mask) | ((data << _Lo) & mask);
this->_M_range_check<_Hi, _Lo>();
auto data = static_cast<max_size_t>(val);
constexpr auto _Length = _Hi - _Lo + 1;
auto mask = make_mask<_Length> << _Lo;
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> {
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 _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> {
static_assert(_Len <= _Nm, "Bit::slice: _Len should be no greater than _Nm");
debug::assert(pos <= _Nm - _Len, "Bit::slice: pos should be less than _Nm - _Len");
return Bit<_Len>(this->_M_data >> pos);
template<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

View File

@ -1,7 +1,7 @@
#pragma once
#include <limits>
#include <cstdint>
#include <concepts>
#include <cstdint>
#include <limits>
namespace dark {
@ -10,45 +10,45 @@ using max_ssize_t = std::int32_t;
static constexpr std::size_t kMaxLength = std::numeric_limits<max_size_t>::digits;
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;
static_assert(_Len <= kMaxLength, "Mask length out of range");
return _Len == kMaxLength ? ~max_size_t(0) : (max_size_t(1) << _Len) - 1;
}
} // namespace dark
namespace dark::concepts {
template <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) {
b(a); // Can implicitly convert
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
View File

@ -0,0 +1,63 @@
#pragma once
#include "module.h"
#include <algorithm>
#include <memory>
#include <random>
#include <vector>
namespace dark {
class CPU {
private:
std::vector<std::unique_ptr<ModuleBase>> mod_owned;
std::vector<ModuleBase *> modules;
public:
unsigned long long cycles = 0;
private:
void sync_all() {
for (auto &module: modules)
module->sync();
}
public:
/// @attention the pointer will be moved. you SHOULD NOT use it after calling this function.
template<typename _Tp>
requires std::derived_from<_Tp, ModuleBase>
void add_module(std::unique_ptr<_Tp> &module) {
modules.push_back(module.get());
mod_owned.emplace_back(std::move(module));
}
void add_module(std::unique_ptr<ModuleBase> module) {
modules.push_back(module.get());
mod_owned.emplace_back(std::move(module));
}
void add_module(ModuleBase *module) {
modules.push_back(module);
}
void run_once() {
++cycles;
for (auto &module: modules)
module->work();
sync_all();
}
void run_once_shuffle() {
static std::default_random_engine engine;
std::vector<ModuleBase *> shuffled = modules;
std::shuffle(shuffled.begin(), shuffled.end(), engine);
++cycles;
for (auto &module: shuffled)
module->work();
sync_all();
}
void run(unsigned long long max_cycles = 0, bool shuffle = false) {
auto func = shuffle ? &CPU::run_once_shuffle : &CPU::run_once;
while (max_cycles == 0 || cycles < max_cycles)
(this->*func)();
}
};
} // namespace dark

View File

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

27
include/module.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "synchronize.h"
namespace dark {
namespace details {
struct empty_class {
void sync() { /* do nothing */ }
};
} // namespace details
struct ModuleBase {
virtual void work() = 0;
virtual void sync() = 0;
virtual ~ModuleBase() = default;
};
template<typename _Tinput, typename _Toutput, typename _Tprivate = details::empty_class>
requires std::is_aggregate_v<_Tinput> && std::is_aggregate_v<_Toutput> && std::is_aggregate_v<_Tprivate>
struct Module : public ModuleBase, public _Tinput, public _Toutput, protected _Tprivate {
void sync() override final {
sync_member(static_cast<_Tinput &>(*this));
sync_member(static_cast<_Toutput &>(*this));
sync_member(static_cast<_Tprivate &>(*this));
}
};
} // namespace dark

View File

@ -3,127 +3,128 @@
namespace dark {
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>) {
return _Tp::_Bit_Len;
} else {
static_assert(bit_type <_Up>, "Invalid common length");
return _Up::_Bit_Len;
}
static_assert(bit_match<_Tp, _Up>);
if constexpr (bit_type<_Tp>) {
return _Tp::_Bit_Len;
}
else {
static_assert(bit_type<_Up>, "Invalid common length");
return _Up::_Bit_Len;
}
}
template <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) {
return ~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) {
return cast(lhs) && cast(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) {
return cast(lhs) || cast(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) {
return cast(lhs) == cast(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) {
return cast(lhs) <=> cast(rhs);
template<int_or_bit _Tp, int_or_bit _Up>
constexpr auto operator<=>(const _Tp &lhs, const _Up &rhs) {
return cast(lhs) <=> cast(rhs);
}

View File

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

View File

@ -1,48 +1,47 @@
#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:
static_assert(0 < _Len && _Len <= kMaxLength,
"Register: _Len must be in range [1, kMaxLength].");
private:
static_assert(0 < _Len && _Len <= kMaxLength,
"Register: _Len must be in range [1, kMaxLength].");
friend class Visitor;
friend class Visitor;
max_size_t _M_old : _Len;
max_size_t _M_new : _Len;
max_size_t _M_old : _Len;
max_size_t _M_new : _Len;
[[no_unique_address]]
debug::DebugValue <bool, false> _M_assigned;
[[no_unique_address]]
debug::DebugValue<bool, false> _M_assigned;
void sync() {
if (this->_M_assigned) {
this->_M_assigned = false;
this->_M_old = this->_M_new;
}
}
void sync() {
this->_M_assigned = false;
this->_M_old = this->_M_new;
}
public:
static constexpr std::size_t _Bit_Len = _Len;
public:
static constexpr std::size_t _Bit_Len = _Len;
Register() : _M_old(), _M_new(), _M_assigned() {}
Register() : _M_old(), _M_new(), _M_assigned() {}
Register(Register &&) = delete;
Register(const Register &) = delete;
Register &operator=(Register &&) = delete;
Register &operator=(const Register &rhs) = delete;
Register(Register &&) = delete;
Register(const Register &) = delete;
Register &operator=(Register &&) = delete;
Register &operator=(const Register &rhs) = delete;
template <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);
}
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);
}
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

View File

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

View File

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

View File

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