Code owners
Assign users and groups as approvers for specific file changes. Learn more.
operation.cpp 4.97 KiB
#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