Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "operation.h"
#include "../debug.h"
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace asic {
signal_source::signal_source(std::shared_ptr<const operation> op, std::size_t index, std::optional<std::size_t> bits)
: m_operation(std::move(op))
, m_index(index)
, m_bits(std::move(bits)) {}
signal_source::operator bool() const noexcept {
return static_cast<bool>(m_operation);
}
std::optional<number> signal_source::current_output(delay_map const& delays) const {
ASIC_ASSERT(m_operation);
return m_operation->current_output(m_index, delays);
}
number signal_source::evaluate_output(evaluation_context const& context) const {
ASIC_ASSERT(m_operation);
return m_operation->evaluate_output(m_index, context);
}
std::optional<std::size_t> signal_source::bits() const noexcept {
return m_bits;
}
abstract_operation::abstract_operation(result_key key)
: m_key(std::move(key)) {}
std::optional<number> abstract_operation::current_output(std::size_t, delay_map const&) const {
return std::nullopt;
}
number abstract_operation::evaluate_output(std::size_t index, evaluation_context const& context) const {
ASIC_ASSERT(index < this->output_count());
ASIC_ASSERT(context.results);
auto const key = this->key_of_output(index);
if (auto const it = context.results->find(key); it != context.results->end()) {
if (it->second) {
return *it->second;
}
throw std::runtime_error{"Direct feedback loop detected when evaluating simulation operation."};
}
auto& result = context.results->try_emplace(key, this->current_output(index, *context.delays))
.first->second; // Use a reference to avoid potential iterator invalidation caused by evaluate_output_impl.
auto const value = this->evaluate_output_impl(index, context);
ASIC_ASSERT(&context.results->at(key) == &result);
result = value;
return value;
}
number abstract_operation::truncate_input(std::size_t index, number value, std::size_t bits) const {
if (value.imag() != 0) {
throw py::type_error{
fmt::format("Complex value cannot be truncated to {} bits as requested by the signal connected to input #{}", bits, index)};
}
if (bits > 64) {
throw py::value_error{
fmt::format("Cannot truncate to {} (more than 64) bits as requested by the singal connected to input #{}", bits, index)};
}
return number{static_cast<number::value_type>(static_cast<std::int64_t>(value.real()) & ((std::int64_t{1} << bits) - 1))};
}
result_key const& abstract_operation::key_base() const {
return m_key;
}
result_key abstract_operation::key_of_output(std::size_t index) const {
if (m_key.empty()) {
return fmt::to_string(index);
}
if (this->output_count() == 1) {
return m_key;
}
return fmt::format("{}.{}", m_key, index);
}
unary_operation::unary_operation(result_key key)
: abstract_operation(std::move(key)) {}
void unary_operation::connect(signal_source in) {
m_in = std::move(in);
}
bool unary_operation::connected() const noexcept {
return static_cast<bool>(m_in);
}
signal_source const& unary_operation::input() const noexcept {
return m_in;
}
number unary_operation::evaluate_input(evaluation_context const& context) const {
auto const value = m_in.evaluate_output(context);
auto const bits = context.bits_override.value_or(m_in.bits().value_or(0));
return (context.truncate && bits) ? this->truncate_input(0, value, bits) : value;
}
binary_operation::binary_operation(result_key key)
: abstract_operation(std::move(key)) {}
void binary_operation::connect(signal_source lhs, signal_source rhs) {
m_lhs = std::move(lhs);
m_rhs = std::move(rhs);
}
signal_source const& binary_operation::lhs() const noexcept {
return m_lhs;
}
signal_source const& binary_operation::rhs() const noexcept {
return m_rhs;
}
number binary_operation::evaluate_lhs(evaluation_context const& context) const {
auto const value = m_lhs.evaluate_output(context);
auto const bits = context.bits_override.value_or(m_lhs.bits().value_or(0));
return (context.truncate && bits) ? this->truncate_input(0, value, bits) : value;
}
number binary_operation::evaluate_rhs(evaluation_context const& context) const {
auto const value = m_rhs.evaluate_output(context);
auto const bits = context.bits_override.value_or(m_rhs.bits().value_or(0));
return (context.truncate && bits) ? this->truncate_input(0, value, bits) : value;
}
nary_operation::nary_operation(result_key key)
: abstract_operation(std::move(key)) {}
void nary_operation::connect(std::vector<signal_source> inputs) {
m_inputs = std::move(inputs);
}
span<signal_source const> nary_operation::inputs() const noexcept {
return m_inputs;
}
std::vector<number> nary_operation::evaluate_inputs(evaluation_context const& context) const {
auto values = std::vector<number>{};
values.reserve(m_inputs.size());
for (auto const& input : m_inputs) {
auto const value = input.evaluate_output(context);
auto const bits = context.bits_override.value_or(input.bits().value_or(0));
values.push_back((context.truncate && bits) ? this->truncate_input(0, value, bits) : value);
}
return values;
}
} // namespace asic