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

63
docs/frame.md Normal file
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
@ -81,13 +82,13 @@ Wire <4> wire2 = [&reg]() -> auto & { return reg; };
wire = []() { return 0b11010; };
// Ill formed! Wire cannot accept a value
// with different bit-width
// 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,9 +1,9 @@
#pragma once
#include "concept.h"
#include "debug.h"
#include <version>
#include <climits>
#include <concepts>
#include <version>
namespace dark {

View File

@ -14,7 +14,9 @@ constexpr auto int_concat(max_size_t arg, auto ...args) {
template<std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto sign_extend(max_size_t val) {
static_assert(_Old < _New, "sign_extend: _Old should be less than _New");
struct { max_ssize_t _M_data : _Old; } tmp;
struct {
max_ssize_t _M_data : _Old;
} tmp;
return Bit<_New>(tmp._M_data = val);
}
@ -26,7 +28,9 @@ constexpr auto sign_extend(const _Tp & val) {
template<std::size_t _Old, std::size_t _New = kMaxLength>
constexpr auto zero_extend(max_size_t val) {
static_assert(_Old < _New, "zero_extend: _Old should be less than _New");
struct { max_size_t _M_data : _Old; } tmp;
struct {
max_size_t _M_data : _Old;
} tmp;
return Bit<_New>(tmp._M_data = val);
}
@ -82,5 +86,4 @@ constexpr auto Bit<_Nm>::slice(std::size_t pos) const -> Bit <_Len> {
}
} // namespace dark

View File

@ -1,7 +1,7 @@
#pragma once
#include <limits>
#include <cstdint>
#include <concepts>
#include <cstdint>
#include <limits>
namespace dark {
@ -30,8 +30,7 @@ concept implicit_convertible_to = requires(_From &a, func_t <_To> b) {
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>
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>
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>
concept bit_convertible =

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

@ -51,6 +51,7 @@ 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; }
@ -61,7 +62,10 @@ struct DebugValue {
#endif
public:
explicit operator _Tp() const { return this->get_value(); }
DebugValue &operator=(_Tp value) { this->set_value(value); return *this; }
DebugValue &operator=(_Tp value) {
this->set_value(value);
return *this;
}
};
} // namespace dark::debug

27
include/module.h Normal file
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,9 +3,9 @@
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) {
@ -17,7 +17,8 @@ consteval auto get_common_length() -> std::size_t {
static_assert(bit_match<_Tp, _Up>);
if constexpr (bit_type<_Tp>) {
return _Tp::_Bit_Len;
} else {
}
else {
static_assert(bit_type<_Up>, "Invalid common length");
return _Up::_Bit_Len;
}

View File

@ -1,79 +1,102 @@
#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... }; }) {
}
else if constexpr (!requires { _Tp{args...}; }) {
return size - 1;
} else {
}
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>
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>
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) {
}
else if constexpr (size == 2) {
auto &[x0, x1] = value;
return std::forward_as_tuple(x0, x1);
} else if constexpr (size == 3) {
}
else if constexpr (size == 3) {
auto &[x0, x1, x2] = value;
return std::forward_as_tuple(x0, x1, x2);
} else if constexpr (size == 4) {
}
else if constexpr (size == 4) {
auto &[x0, x1, x2, x3] = value;
return std::forward_as_tuple(x0, x1, x2, x3);
} else if constexpr (size == 5) {
}
else if constexpr (size == 5) {
auto &[x0, x1, x2, x3, x4] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4);
} else if constexpr (size == 6) {
}
else if constexpr (size == 6) {
auto &[x0, x1, x2, x3, x4, x5] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5);
} else if constexpr (size == 7) {
}
else if constexpr (size == 7) {
auto &[x0, x1, x2, x3, x4, x5, x6] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6);
} else if constexpr (size == 8) {
}
else if constexpr (size == 8) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7);
} else if constexpr (size == 9) {
}
else if constexpr (size == 9) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8);
} else if constexpr (size == 10) {
}
else if constexpr (size == 10) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9);
} else if constexpr (size == 11) {
}
else if constexpr (size == 11) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);
} else if constexpr (size == 12) {
}
else if constexpr (size == 12) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11);
} else if constexpr (size == 13) {
}
else if constexpr (size == 13) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12);
} else if constexpr (size == 14) {
}
else if constexpr (size == 14) {
auto &[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13] = value;
return std::forward_as_tuple(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13);
} else {
}
else {
static_assert(sizeof(_Tp) == 0, "The struct has too many members.");
}
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "debug.h"
#include "concept.h"
#include "debug.h"
namespace dark {
@ -19,11 +19,9 @@ struct Register {
debug::DebugValue<bool, false> _M_assigned;
void sync() {
if (this->_M_assigned) {
this->_M_assigned = false;
this->_M_old = this->_M_new;
}
}
public:
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 bool() const { return this->_M_old; }
};
} // namespace dark

View File

@ -9,7 +9,8 @@ struct Visitor {
static constexpr bool is_syncable_v =
requires(_Tp &val) { { val.sync() } -> std::same_as<void>; };
template <typename _Tp> requires is_syncable_v<_Tp>
template<typename _Tp>
requires is_syncable_v<_Tp>
static void sync(_Tp &val) { val.sync(); }
template<typename _Tp, typename _Base>
@ -43,18 +44,23 @@ 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>) {
}
else if constexpr (is_std_array_v<_Tp>) {
for (auto &member: value) sync_member(member);
} else if constexpr (Visitor::is_syncable_v<_Tp>) {
}
else if constexpr (Visitor::is_syncable_v<_Tp>) {
Visitor::sync(value);
} else if constexpr (has_valid_tag<_Tp>) {
}
else if constexpr (has_valid_tag<_Tp>) {
sync_by_tag(value, typename _Tp::Tags{});
} else if constexpr (std::is_aggregate_v<_Tp>) {
}
else if constexpr (std::is_aggregate_v<_Tp>) {
auto &&tuple = reflect::tuplify(value);
std::apply([](auto &...members) { (sync_member(members), ...); }, tuple);
} else {
}
else {
static_assert(sizeof(_Tp) == 0, "This type is not syncable.");
}
}
} // namespace dark::hardware
} // namespace dark

View File

@ -1,20 +1,22 @@
#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;
@ -27,5 +29,5 @@ constexpr auto to_unsigned(const _Tp &x) {
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,6 +1,6 @@
#pragma once
#include "debug.h"
#include "concept.h"
#include "debug.h"
#include <memory>
namespace dark {
@ -59,6 +59,7 @@ struct Wire {
[[no_unique_address]]
debug::DebugValue<bool, false> _M_assigned;
private:
void sync() { this->_M_holds = false; }
template<details::WireFunction<_Len> _Fn>
@ -75,8 +76,7 @@ struct Wire {
public:
static constexpr std::size_t _Bit_Len = _Len;
Wire() :
_M_func(new details::EmptyWire),
Wire() : _M_func(new details::EmptyWire),
_M_cache(), _M_holds(), _M_assigned() {}
explicit operator max_size_t() const {
@ -93,8 +93,7 @@ struct Wire {
Wire &operator=(const Wire &rhs) = delete;
template<details::WireFunction<_Len> _Fn>
Wire(_Fn &&fn) :
_M_func(_M_new_func(std::forward <_Fn> (fn))),
Wire(_Fn &&fn) : _M_func(_M_new_func(std::forward<_Fn>(fn))),
_M_cache(), _M_holds(), _M_assigned() {}
template<details::WireFunction<_Len> _Fn>
@ -108,6 +107,10 @@ struct Wire {
this->_M_func.reset(_M_new_func(std::forward<_Fn>(fn)));
this->sync();
}
explicit operator bool() const {
return static_cast<max_size_t>(*this);
}
};