diff --git a/CMakeLists.txt b/CMakeLists.txt index 256d9973098f7681fb9c63602f8f9f0b614fa008..e0d087c07656292751f53b55abd2bdea0feeaeaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project( find_package(fmt 5.2.1 REQUIRED) find_package(pybind11 CONFIG REQUIRED) -set(LIBRARY_NAME "b_asic") # Name of the python folder. +set(LIBRARY_NAME "b_asic") # Name of the python library directory. set(TARGET_NAME "_${LIBRARY_NAME}") # Name of this extension module. # Set output directory for compiled binaries. diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index 06c190caa1a8560f90626ded013855dbd017077b..30dbba97c10e069b2216f44c2dd3c5771207094d 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -3,17 +3,20 @@ B-ASIC Signal Flow Graph Module. TODO: More info. """ -from typing import List, Iterable, Sequence, Dict, Optional, DefaultDict, MutableSet +from typing import List, Iterable, Sequence, Dict, Optional, DefaultDict, MutableSet, Tuple from numbers import Number from collections import defaultdict, deque from b_asic.port import SignalSourceProvider, OutputPort -from b_asic.operation import Operation, AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap +from b_asic.operation import Operation, AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap, ResultKey from b_asic.signal import Signal from b_asic.graph_component import GraphID, GraphIDNumber, GraphComponent, Name, TypeName from b_asic.special_operations import Input, Output +DelayQueue = List[Tuple[str, ResultKey, OutputPort]] + + class GraphIDGenerator: """A class that generates Graph IDs for objects.""" @@ -173,7 +176,7 @@ class SFG(AbstractOperation): else: output_string += "-, " - if component.type_name is "c": + if component.type_name == "c": output_string += "value: " + str(component.value) + ", input: [" else: output_string += "input: [" @@ -230,7 +233,13 @@ class SFG(AbstractOperation): for op, arg in zip(self._input_operations, self.truncate_inputs(input_values, bits_override) if truncate else input_values): op.value = arg - value = self._evaluate_source(self._output_operations[index].input(0).signals[0].source, results, delays, prefix, bits_override, truncate) + deferred_delays = [] + value = self._evaluate_source(self._output_operations[index].input(0).signals[0].source, results, delays, prefix, bits_override, truncate, deferred_delays) + while deferred_delays: + new_deferred_delays = [] + for key_base, key, src in deferred_delays: + self._do_evaluate_source(key_base, key, src, results, delays, prefix, bits_override, truncate, new_deferred_delays) + deferred_delays = new_deferred_delays results[self.key(index, prefix)] = value return value @@ -422,22 +431,26 @@ class SFG(AbstractOperation): # The old SFG will be deleted by Python GC return self() - def _evaluate_source(self, src: OutputPort, results: MutableResultMap, delays: MutableDelayMap, prefix: str, bits_override: Optional[int], truncate: bool) -> Number: - src_prefix = prefix - if src_prefix: - src_prefix += "." - src_prefix += src.operation.graph_id - - key = src.operation.key(src.index, src_prefix) + def _evaluate_source(self, src: OutputPort, results: MutableResultMap, delays: MutableDelayMap, prefix: str, bits_override: Optional[int], truncate: bool, deferred_delays: DelayQueue) -> Number: + key_base = (prefix + "." + src.operation.graph_id) if prefix else src.operation.graph_id + key = src.operation.key(src.index, key_base) if key in results: value = results[key] if value is None: - raise RuntimeError(f"Direct feedback loop detected when evaluating operation.") + raise RuntimeError("Direct feedback loop detected when evaluating operation.") return value + + value = src.operation.current_output(src.index, delays, key_base) + results[key] = value + if value is None: + value = self._do_evaluate_source(key_base, key, src, results, delays, prefix, bits_override, truncate, deferred_delays) + else: + deferred_delays.append((key_base, key, src)) # Evaluate later. Use current value for now. + return value - results[key] = src.operation.current_output(src.index, delays, src_prefix) - input_values = [self._evaluate_source(input_port.signals[0].source, results, delays, prefix, bits_override, truncate) for input_port in src.operation.inputs] - value = src.operation.evaluate_output(src.index, input_values, results, delays, src_prefix, bits_override, truncate) + def _do_evaluate_source(self, key_base: str, key: ResultKey, src: OutputPort, results: MutableResultMap, delays: MutableDelayMap, prefix: str, bits_override: Optional[int], truncate: bool, deferred_delays: DelayQueue) -> Number: + input_values = [self._evaluate_source(input_port.signals[0].source, results, delays, prefix, bits_override, truncate, deferred_delays) for input_port in src.operation.inputs] + value = src.operation.evaluate_output(src.index, input_values, results, delays, key_base, bits_override, truncate) results[key] = value return value diff --git a/legacy/simulation_oop/core_operations.h b/legacy/simulation_oop/core_operations.h index ca871b7f7ea03d87d5e085e5581a1776b2964a57..b2263986a6c3b484ebcf10a48d48ec939dadac86 100644 --- a/legacy/simulation_oop/core_operations.h +++ b/legacy/simulation_oop/core_operations.h @@ -24,7 +24,7 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map&, delay_map&, std::optional<std::size_t>, bool) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const&) const final { ASIC_DEBUG_MSG("Evaluating constant."); return m_value; } @@ -42,9 +42,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating addition."); - return this->evaluate_lhs(results, delays, bits_override, truncate) + this->evaluate_rhs(results, delays, bits_override, truncate); + return this->evaluate_lhs(context) + this->evaluate_rhs(context); } }; @@ -58,9 +58,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating subtraction."); - return this->evaluate_lhs(results, delays, bits_override, truncate) - this->evaluate_rhs(results, delays, bits_override, truncate); + return this->evaluate_lhs(context) - this->evaluate_rhs(context); } }; @@ -74,9 +74,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating multiplication."); - return this->evaluate_lhs(results, delays, bits_override, truncate) * this->evaluate_rhs(results, delays, bits_override, truncate); + return this->evaluate_lhs(context) * this->evaluate_rhs(context); } }; @@ -90,9 +90,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating division."); - return this->evaluate_lhs(results, delays, bits_override, truncate) / this->evaluate_rhs(results, delays, bits_override, truncate); + return this->evaluate_lhs(context) / this->evaluate_rhs(context); } }; @@ -106,13 +106,13 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating min."); - auto const lhs = this->evaluate_lhs(results, delays, bits_override, truncate); + auto const lhs = this->evaluate_lhs(context); if (lhs.imag() != 0) { throw std::runtime_error{"Min does not support complex numbers."}; } - auto const rhs = this->evaluate_rhs(results, delays, bits_override, truncate); + auto const rhs = this->evaluate_rhs(context); if (rhs.imag() != 0) { throw std::runtime_error{"Min does not support complex numbers."}; } @@ -130,13 +130,13 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating max."); - auto const lhs = this->evaluate_lhs(results, delays, bits_override, truncate); + auto const lhs = this->evaluate_lhs(context); if (lhs.imag() != 0) { throw std::runtime_error{"Max does not support complex numbers."}; } - auto const rhs = this->evaluate_rhs(results, delays, bits_override, truncate); + auto const rhs = this->evaluate_rhs(context); if (rhs.imag() != 0) { throw std::runtime_error{"Max does not support complex numbers."}; } @@ -154,9 +154,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating sqrt."); - return std::sqrt(this->evaluate_input(results, delays, bits_override, truncate)); + return std::sqrt(this->evaluate_input(context)); } }; @@ -170,9 +170,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating conj."); - return std::conj(this->evaluate_input(results, delays, bits_override, truncate)); + return std::conj(this->evaluate_input(context)); } }; @@ -186,9 +186,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating abs."); - return std::abs(this->evaluate_input(results, delays, bits_override, truncate)); + return std::abs(this->evaluate_input(context)); } }; @@ -203,9 +203,9 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating cmul."); - return this->evaluate_input(results, delays, bits_override, truncate) * m_value; + return this->evaluate_input(context) * m_value; } number m_value; @@ -221,12 +221,12 @@ public: } private: - [[nodiscard]] number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final { + [[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final { ASIC_DEBUG_MSG("Evaluating bfly."); if (index == 0) { - return this->evaluate_lhs(results, delays, bits_override, truncate) + this->evaluate_rhs(results, delays, bits_override, truncate); + return this->evaluate_lhs(context) + this->evaluate_rhs(context); } - return this->evaluate_lhs(results, delays, bits_override, truncate) - this->evaluate_rhs(results, delays, bits_override, truncate); + return this->evaluate_lhs(context) - this->evaluate_rhs(context); } }; diff --git a/legacy/simulation_oop/custom_operation.cpp b/legacy/simulation_oop/custom_operation.cpp index 9308e0b6fc0e9836ba550c23c68e4d266a1a02cf..9153eb5b651f3a481747f3b652f790ce581e70dc 100644 --- a/legacy/simulation_oop/custom_operation.cpp +++ b/legacy/simulation_oop/custom_operation.cpp @@ -1,11 +1,13 @@ #include "custom_operation.h" + #include <pybind11/stl.h> namespace py = pybind11; namespace asic { -custom_operation::custom_operation(result_key key, pybind11::object evaluate_output, pybind11::object truncate_input, std::size_t output_count) +custom_operation::custom_operation(result_key key, pybind11::object evaluate_output, pybind11::object truncate_input, + std::size_t output_count) : nary_operation(std::move(key)) , m_evaluate_output(std::move(evaluate_output)) , m_truncate_input(std::move(truncate_input)) @@ -15,9 +17,9 @@ std::size_t custom_operation::output_count() const noexcept { return m_output_count; } -number custom_operation::evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +number custom_operation::evaluate_output_impl(std::size_t index, evaluation_context const& context) const { using namespace pybind11::literals; - auto input_values = this->evaluate_inputs(results, delays, bits_override, truncate); + auto input_values = this->evaluate_inputs(context); return m_evaluate_output(index, std::move(input_values), "truncate"_a = false).cast<number>(); } diff --git a/legacy/simulation_oop/custom_operation.h b/legacy/simulation_oop/custom_operation.h index a083bc3166d9c975cfb6f862b539bebc0a8b1049..8a11aaacbc8c17500069522d9d8f56d9c416d804 100644 --- a/legacy/simulation_oop/custom_operation.h +++ b/legacy/simulation_oop/custom_operation.h @@ -22,7 +22,7 @@ public: [[nodiscard]] std::size_t output_count() const noexcept final; private: - [[nodiscard]] number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final; [[nodiscard]] number truncate_input(std::size_t index, number value, std::size_t bits) const final; pybind11::object m_evaluate_output; diff --git a/legacy/simulation_oop/operation.cpp b/legacy/simulation_oop/operation.cpp index 33fc5d5bb76606331ab94a5c354395ae65b840e6..a9738a0a05287f6ab2d430d4c73560a4c6bd57c5 100644 --- a/legacy/simulation_oop/operation.cpp +++ b/legacy/simulation_oop/operation.cpp @@ -1,4 +1,5 @@ #include "operation.h" + #include "../debug.h" #include <pybind11/pybind11.h> @@ -21,9 +22,9 @@ std::optional<number> signal_source::current_output(delay_map const& delays) con return m_operation->current_output(m_index, delays); } -number signal_source::evaluate_output(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +number signal_source::evaluate_output(evaluation_context const& context) const { ASIC_ASSERT(m_operation); - return m_operation->evaluate_output(m_index, results, delays, bits_override, truncate); + return m_operation->evaluate_output(m_index, context); } std::optional<std::size_t> signal_source::bits() const noexcept { @@ -37,19 +38,20 @@ std::optional<number> abstract_operation::current_output(std::size_t, delay_map return std::nullopt; } -number abstract_operation::evaluate_output(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +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 = results.find(key); it != results.end()) { + 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 = results.try_emplace(key, this->current_output(index, delays)) + 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, results, delays, bits_override, truncate); - ASIC_ASSERT(&results.at(key) == &result); + auto const value = this->evaluate_output_impl(index, context); + ASIC_ASSERT(&context.results->at(key) == &result); result = value; return value; } @@ -91,10 +93,14 @@ bool unary_operation::connected() const noexcept { return static_cast<bool>(m_in); } -number unary_operation::evaluate_input(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { - auto const value = m_in.evaluate_output(results, delays, bits_override, truncate); - auto const bits = bits_override.value_or(m_in.bits().value_or(0)); - return (truncate && bits) ? this->truncate_input(0, value, bits) : value; +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) @@ -105,16 +111,24 @@ void binary_operation::connect(signal_source lhs, signal_source rhs) { m_rhs = std::move(rhs); } -number binary_operation::evaluate_lhs(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { - auto const value = m_lhs.evaluate_output(results, delays, bits_override, truncate); - auto const bits = bits_override.value_or(m_lhs.bits().value_or(0)); - return (truncate && bits) ? this->truncate_input(0, value, bits) : value; +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_rhs(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { - auto const value = m_rhs.evaluate_output(results, delays, bits_override, truncate); - auto const bits = bits_override.value_or(m_rhs.bits().value_or(0)); - return (truncate && bits) ? this->truncate_input(0, value, bits) : value; +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) @@ -124,13 +138,17 @@ void nary_operation::connect(std::vector<signal_source> inputs) { m_inputs = std::move(inputs); } -std::vector<number> nary_operation::evaluate_inputs(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +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(results, delays, bits_override, truncate); - auto const bits = bits_override.value_or(input.bits().value_or(0)); - values.push_back((truncate && bits) ? this->truncate_input(0, value, bits) : value); + 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; } diff --git a/legacy/simulation_oop/operation.h b/legacy/simulation_oop/operation.h index 152383104a1a954c0f5edebf7de5e9288341650e..344eacc1482c40021b3d0ff686cbef5c71085f58 100644 --- a/legacy/simulation_oop/operation.h +++ b/legacy/simulation_oop/operation.h @@ -2,6 +2,7 @@ #define ASIC_SIMULATION_OPERATION_H #include "../number.h" +#include "../span.h" #include <cstddef> #include <cstdint> @@ -16,17 +17,20 @@ namespace asic { +class operation; +class signal_source; + using result_key = std::string; using result_map = std::unordered_map<result_key, std::optional<number>>; using delay_map = std::unordered_map<result_key, number>; - -class operation { -public: - virtual ~operation() = default; - [[nodiscard]] virtual std::size_t output_count() const noexcept = 0; - [[nodiscard]] virtual std::optional<number> current_output(std::size_t index, delay_map const& delays) const = 0; - [[nodiscard]] virtual number evaluate_output(std::size_t index, result_map& results, delay_map& delays, - std::optional<std::size_t> bits_override, bool truncate) const = 0; +using delay_queue = std::vector<std::pair<result_key, signal_source const*>>; + +struct evaluation_context final { + result_map* results; + delay_map* delays; + delay_queue* deferred_delays; + std::optional<std::size_t> bits_override; + bool truncate; }; class signal_source final { @@ -37,8 +41,7 @@ public: [[nodiscard]] explicit operator bool() const noexcept; [[nodiscard]] std::optional<number> current_output(delay_map const& delays) const; - [[nodiscard]] number evaluate_output(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, - bool truncate) const; + [[nodiscard]] number evaluate_output(evaluation_context const& context) const; [[nodiscard]] std::optional<std::size_t> bits() const noexcept; @@ -48,18 +51,24 @@ private: std::optional<std::size_t> m_bits; }; +class operation { +public: + virtual ~operation() = default; + [[nodiscard]] virtual std::size_t output_count() const noexcept = 0; + [[nodiscard]] virtual std::optional<number> current_output(std::size_t index, delay_map const& delays) const = 0; + [[nodiscard]] virtual number evaluate_output(std::size_t index, evaluation_context const& context) const = 0; +}; + class abstract_operation : public operation { public: explicit abstract_operation(result_key key); virtual ~abstract_operation() = default; [[nodiscard]] std::optional<number> current_output(std::size_t, delay_map const&) const override; - [[nodiscard]] number evaluate_output(std::size_t index, result_map& results, delay_map& delays, - std::optional<std::size_t> bits_override, bool truncate) const override; + [[nodiscard]] number evaluate_output(std::size_t index, evaluation_context const& context) const override; protected: - [[nodiscard]] virtual number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, - std::optional<std::size_t> bits_override, bool truncate) const = 0; + [[nodiscard]] virtual number evaluate_output_impl(std::size_t index, evaluation_context const& context) const = 0; [[nodiscard]] virtual number truncate_input(std::size_t index, number value, std::size_t bits) const; [[nodiscard]] result_key const& key_base() const; @@ -78,9 +87,8 @@ public: protected: [[nodiscard]] bool connected() const noexcept; - - [[nodiscard]] number evaluate_input(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, - bool truncate) const; + [[nodiscard]] signal_source const& input() const noexcept; + [[nodiscard]] number evaluate_input(evaluation_context const& context) const; private: signal_source m_in; @@ -94,10 +102,10 @@ public: void connect(signal_source lhs, signal_source rhs); protected: - [[nodiscard]] number evaluate_lhs(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, - bool truncate) const; - [[nodiscard]] number evaluate_rhs(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, - bool truncate) const; + [[nodiscard]] signal_source const& lhs() const noexcept; + [[nodiscard]] signal_source const& rhs() const noexcept; + [[nodiscard]] number evaluate_lhs(evaluation_context const& context) const; + [[nodiscard]] number evaluate_rhs(evaluation_context const& context) const; private: signal_source m_lhs; @@ -112,8 +120,8 @@ public: void connect(std::vector<signal_source> inputs); protected: - [[nodiscard]] std::vector<number> evaluate_inputs(result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, - bool truncate) const; + [[nodiscard]] span<signal_source const> inputs() const noexcept; + [[nodiscard]] std::vector<number> evaluate_inputs(evaluation_context const& context) const; private: std::vector<signal_source> m_inputs; diff --git a/legacy/simulation_oop/signal_flow_graph.cpp b/legacy/simulation_oop/signal_flow_graph.cpp index 5e237e6866b76dd383643e53c239257afee67b58..d62ff6d33c09f04abee63659e8fcf4c0f18ac5c7 100644 --- a/legacy/simulation_oop/signal_flow_graph.cpp +++ b/legacy/simulation_oop/signal_flow_graph.cpp @@ -1,4 +1,5 @@ #include "signal_flow_graph.h" + #include "../debug.h" namespace py = pybind11; @@ -30,12 +31,12 @@ std::size_t signal_flow_graph_operation::output_count() const noexcept { return m_output_operations.size(); } -number signal_flow_graph_operation::evaluate_output(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +number signal_flow_graph_operation::evaluate_output(std::size_t index, evaluation_context const& context) const { ASIC_DEBUG_MSG("Evaluating SFG."); - return m_output_operations.at(index).evaluate_output(0, results, delays, bits_override, truncate); + return m_output_operations.at(index).evaluate_output(0, context); } -number signal_flow_graph_operation::evaluate_output_impl(std::size_t, result_map&, delay_map&, std::optional<std::size_t>, bool) const { +number signal_flow_graph_operation::evaluate_output_impl(std::size_t, evaluation_context const&) const { return number{}; } @@ -66,7 +67,8 @@ std::shared_ptr<custom_operation> signal_flow_graph_operation::add_custom_operat std::string_view prefix, result_key key) { auto const input_count = op.attr("input_count").cast<std::size_t>(); auto const output_count = op.attr("output_count").cast<std::size_t>(); - auto const new_op = add_operation<custom_operation>(op, added, key, op.attr("evaluate_output"), op.attr("truncate_input"), output_count); + auto const new_op = add_operation<custom_operation>( + op, added, key, op.attr("evaluate_output"), op.attr("truncate_input"), output_count); auto inputs = std::vector<signal_source>{}; inputs.reserve(input_count); for (auto const i : range(input_count)) { diff --git a/legacy/simulation_oop/signal_flow_graph.h b/legacy/simulation_oop/signal_flow_graph.h index 83c62482b8534d933051ee1e4961701fe00e3838..f06788249e367d3e8e0602f04c6dcf91c71b7a96 100644 --- a/legacy/simulation_oop/signal_flow_graph.h +++ b/legacy/simulation_oop/signal_flow_graph.h @@ -34,10 +34,10 @@ public: [[nodiscard]] std::vector<std::shared_ptr<input_operation>> const& inputs() noexcept; [[nodiscard]] std::size_t output_count() const noexcept final; - [[nodiscard]] number evaluate_output(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output(std::size_t index, evaluation_context const& context) const final; private: - [[nodiscard]] number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final; [[nodiscard]] static signal_source make_source(pybind11::handle op, std::size_t input_index, added_operation_cache& added, std::string_view prefix); diff --git a/legacy/simulation_oop/simulation.cpp b/legacy/simulation_oop/simulation.cpp index 52fd9c2ec40fd7650f516f6e241fa52b2daea797..35805bf99f9a0d295e65a5334e8d6b8440eaf5a1 100644 --- a/legacy/simulation_oop/simulation.cpp +++ b/legacy/simulation_oop/simulation.cpp @@ -1,5 +1,7 @@ #include "simulation.h" +#include "../debug.h" + namespace py = pybind11; namespace asic { @@ -52,19 +54,41 @@ std::vector<number> simulation::step(bool save_results, std::optional<std::size_ return this->run_for(1, save_results, bits_override, truncate); } -std::vector<number> simulation::run_until(iteration_t iteration, bool save_results, std::optional<std::size_t> bits_override, bool truncate) { +std::vector<number> simulation::run_until(iteration_t iteration, bool save_results, std::optional<std::size_t> bits_override, + bool truncate) { auto result = std::vector<number>{}; while (m_iteration < iteration) { ASIC_DEBUG_MSG("Running simulation iteration."); for (auto&& [input, function] : zip(m_sfg.inputs(), m_input_functions)) { input->value(function(m_iteration)); } - auto results = result_map{}; + result.clear(); result.reserve(m_sfg.output_count()); + + auto results = result_map{}; + auto deferred_delays = delay_queue{}; + auto context = evaluation_context{}; + context.results = &results; + context.delays = &m_delays; + context.deferred_delays = &deferred_delays; + context.bits_override = bits_override; + context.truncate = truncate; + for (auto const i : range(m_sfg.output_count())) { - result.push_back(m_sfg.evaluate_output(i, results, m_delays, bits_override, truncate)); + result.push_back(m_sfg.evaluate_output(i, context)); } + + while (!deferred_delays.empty()) { + auto new_deferred_delays = delay_queue{}; + context.deferred_delays = &new_deferred_delays; + for (auto const& [key, src] : deferred_delays) { + ASIC_ASSERT(src); + m_delays[key] = src->evaluate_output(context); + } + deferred_delays = std::move(new_deferred_delays); + } + if (save_results) { for (auto const& [key, value] : results) { m_results[key].push_back(value.value()); @@ -75,7 +99,8 @@ std::vector<number> simulation::run_until(iteration_t iteration, bool save_resul return result; } -std::vector<number> simulation::run_for(iteration_t iterations, bool save_results, std::optional<std::size_t> bits_override, bool truncate) { +std::vector<number> simulation::run_for(iteration_t iterations, bool save_results, std::optional<std::size_t> bits_override, + bool truncate) { if (iterations > std::numeric_limits<iteration_t>::max() - m_iteration) { throw py::value_error("Simulation iteration type overflow!"); } diff --git a/legacy/simulation_oop/simulation.h b/legacy/simulation_oop/simulation.h index 987f2d9da320934d398f59c6a9c09d9647f2bf08..38e2771b877772bd28048cae16d791bb4e0b45e3 100644 --- a/legacy/simulation_oop/simulation.h +++ b/legacy/simulation_oop/simulation.h @@ -8,7 +8,6 @@ #include "signal_flow_graph.h" #include "special_operations.h" -#include <pybind11/pybind11.h> #include <cstddef> #include <cstdint> #include <fmt/format.h> @@ -18,6 +17,7 @@ #include <optional> #include <pybind11/functional.h> #include <pybind11/numpy.h> +#include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <string_view> #include <unordered_map> @@ -40,8 +40,10 @@ public: void set_inputs(std::vector<std::optional<input_provider_t>> input_providers); [[nodiscard]] std::vector<number> step(bool save_results, std::optional<std::size_t> bits_override, bool truncate); - [[nodiscard]] std::vector<number> run_until(iteration_t iteration, bool save_results, std::optional<std::size_t> bits_override, bool truncate); - [[nodiscard]] std::vector<number> run_for(iteration_t iterations, bool save_results, std::optional<std::size_t> bits_override, bool truncate); + [[nodiscard]] std::vector<number> run_until(iteration_t iteration, bool save_results, std::optional<std::size_t> bits_override, + bool truncate); + [[nodiscard]] std::vector<number> run_for(iteration_t iterations, bool save_results, std::optional<std::size_t> bits_override, + bool truncate); [[nodiscard]] std::vector<number> run(bool save_results, std::optional<std::size_t> bits_override, bool truncate); [[nodiscard]] iteration_t iteration() const noexcept; diff --git a/legacy/simulation_oop/special_operations.cpp b/legacy/simulation_oop/special_operations.cpp index a4595471d08ababb54b7672a3781170aebcd55cf..1f7a6519a90ba08224585e36093694becf495c4d 100644 --- a/legacy/simulation_oop/special_operations.cpp +++ b/legacy/simulation_oop/special_operations.cpp @@ -1,4 +1,5 @@ #include "special_operations.h" + #include "../debug.h" namespace asic { @@ -18,10 +19,10 @@ void input_operation::value(number value) noexcept { m_value = value; } -number input_operation::evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +number input_operation::evaluate_output_impl(std::size_t, evaluation_context const& context) const { ASIC_DEBUG_MSG("Evaluating input."); if (this->connected()) { - return this->evaluate_input(results, delays, bits_override, truncate); + return this->evaluate_input(context); } return m_value; } @@ -33,9 +34,9 @@ std::size_t output_operation::output_count() const noexcept { return 1; } -number output_operation::evaluate_output_impl(std::size_t, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +number output_operation::evaluate_output_impl(std::size_t, evaluation_context const& context) const { ASIC_DEBUG_MSG("Evaluating output."); - return this->evaluate_input(results, delays, bits_override, truncate); + return this->evaluate_input(context); } delay_operation::delay_operation(result_key key, number initial_value) @@ -54,25 +55,23 @@ std::optional<number> delay_operation::current_output(std::size_t index, delay_m return m_initial_value; } -number delay_operation::evaluate_output(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const { +number delay_operation::evaluate_output(std::size_t index, evaluation_context const& context) const { ASIC_DEBUG_MSG("Evaluating delay."); ASIC_ASSERT(index == 0); - auto const key = this->key_of_output(index); - // Use a reference to avoid iterator invalidation by evaluate_input. - auto& delay = delays.try_emplace(key, m_initial_value).first->second; - const auto& [it, inserted] = results.try_emplace(key, delay); - if (!inserted) { - return it->second.value(); + ASIC_ASSERT(context.results); + ASIC_ASSERT(context.delays); + ASIC_ASSERT(context.deferred_delays); + auto key = this->key_of_output(index); + auto const value = context.delays->try_emplace(key, m_initial_value).first->second; + auto const& [it, inserted] = context.results->try_emplace(key, value); + if (inserted) { + context.deferred_delays->emplace_back(std::move(key), &this->input()); + return value; } - // Use a reference to avoid iterator invalidation by evaluate_input. - auto& result = it->second; - const auto value = delay; - delay = this->evaluate_input(results, delays, bits_override, truncate); - result = value; - return value; + return it->second.value(); } -[[nodiscard]] number delay_operation::evaluate_output_impl(std::size_t, result_map&, delay_map&, std::optional<std::size_t>, bool) const { +[[nodiscard]] number delay_operation::evaluate_output_impl(std::size_t, evaluation_context const&) const { return number{}; } diff --git a/legacy/simulation_oop/special_operations.h b/legacy/simulation_oop/special_operations.h index 78ddfd240cb8156e3046aba337121db84cdb56ae..88fb087e84378e36f423364d2c7d83a083828784 100644 --- a/legacy/simulation_oop/special_operations.h +++ b/legacy/simulation_oop/special_operations.h @@ -20,7 +20,7 @@ public: void value(number value) noexcept; private: - [[nodiscard]] number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final; number m_value{}; }; @@ -32,7 +32,7 @@ public: [[nodiscard]] std::size_t output_count() const noexcept final; private: - [[nodiscard]] number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final; }; class delay_operation final : public unary_operation { @@ -42,10 +42,10 @@ public: [[nodiscard]] std::size_t output_count() const noexcept final; [[nodiscard]] std::optional<number> current_output(std::size_t index, delay_map const& delays) const final; - [[nodiscard]] number evaluate_output(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output(std::size_t index, evaluation_context const& context) const final; private: - [[nodiscard]] number evaluate_output_impl(std::size_t index, result_map& results, delay_map& delays, std::optional<std::size_t> bits_override, bool truncate) const final; + [[nodiscard]] number evaluate_output_impl(std::size_t index, evaluation_context const& context) const final; number m_initial_value; }; diff --git a/src/debug.h b/src/debug.h index f1ad2d193d2021b6c54784bc1089c5f6c32aea96..a11aa057db644dbe2d29399398a1f48ca599876f 100644 --- a/src/debug.h +++ b/src/debug.h @@ -3,13 +3,9 @@ #ifndef NDEBUG #define ASIC_ENABLE_DEBUG_LOGGING 1 -#else -#define ASIC_ENABLE_DEBUG_LOGGING 0 -#endif // NDEBUG - -#ifndef NDEBUG #define ASIC_ENABLE_ASSERTS 1 #else +#define ASIC_ENABLE_DEBUG_LOGGING 0 #define ASIC_ENABLE_ASSERTS 0 #endif // NDEBUG @@ -50,13 +46,18 @@ inline void log_debug_msg(std::string_view file, int line, Format&& format, Args #endif // ASIC_ENABLE_DEBUG_LOGGING #if ASIC_ENABLE_ASSERTS -inline void log_assert(std::string_view file, int line, bool condition, std::string_view condition_string) { - if (!condition) { +inline void fail_assert(std::string_view file, int line, std::string_view condition_string) { #if ASIC_ENABLE_DEBUG_LOGGING - log_debug_msg(file, line, "Assertion failed: {}", condition_string); + log_debug_msg(file, line, "Assertion failed: {}", condition_string); #endif // ASIC_ENABLE_DEBUG_LOGGING - fmt::print(stderr, "{}:{}: Assertion failed: {}\n", std::filesystem::path{file}.filename().generic_string(), line, condition_string); - std::abort(); + fmt::print(stderr, "{}:{}: Assertion failed: {}\n", std::filesystem::path{file}.filename().generic_string(), line, condition_string); + std::abort(); +} + +template <typename BoolConvertible> +inline void check_assert(std::string_view file, int line, std::string_view condition_string, BoolConvertible&& condition) { + if (!static_cast<bool>(condition)) { + fail_assert(file, line, condition_string); } } #endif // ASIC_ENABLE_ASSERTS @@ -71,7 +72,7 @@ inline void log_assert(std::string_view file, int line, bool condition, std::str #endif // ASIC_ENABLE_DEBUG_LOGGING #if ASIC_ENABLE_ASSERTS -#define ASIC_ASSERT(condition) (asic::detail::log_assert(__FILE__, __LINE__, (condition), #condition)) +#define ASIC_ASSERT(condition) (asic::detail::check_assert(__FILE__, __LINE__, #condition, (condition))) #else #define ASIC_ASSERT(condition) ((void)0) #endif diff --git a/src/simulation/compile.cpp b/src/simulation/compile.cpp index 31538a87d293ffa109108dac6d8d1107f9c07685..33f9cc0c1f5f26dc5f4b2d0b70d7cc0dbae8a163 100644 --- a/src/simulation/compile.cpp +++ b/src/simulation/compile.cpp @@ -10,6 +10,7 @@ #include <limits> #include <optional> #include <string_view> +#include <tuple> #include <unordered_map> #include <utility> @@ -38,9 +39,20 @@ public: simulation_code compile(pybind11::handle sfg) { ASIC_DEBUG_MSG("Compiling code..."); this->initialize_code(sfg.attr("input_count").cast<std::size_t>(), sfg.attr("output_count").cast<std::size_t>()); + auto deferred_delays = delay_queue{}; for (auto const i : range(m_code.output_count)) { - this->add_operation_output(sfg, i, std::string_view{}, sfg_info_stack{}); + this->add_operation_output(sfg, i, std::string_view{}, sfg_info_stack{}, deferred_delays); } + + while (!deferred_delays.empty()) { + auto new_deferred_delays = delay_queue{}; + for (auto const& [delay_index, op, prefix, sfg_stack] : deferred_delays) { + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); + this->add_instruction(instruction_type::update_delay, no_result_index, -1).index = delay_index; + } + deferred_delays = new_deferred_delays; + } + this->resolve_invalid_result_indices(); ASIC_DEBUG_MSG("Compiled code:\n{}\n", format_compiled_simulation_code(m_code)); return std::move(m_code); @@ -66,6 +78,7 @@ private: }; using sfg_info_stack = std::vector<sfg_info>; + using delay_queue = std::vector<std::tuple<std::size_t, py::handle, std::string, sfg_info_stack>>; using added_output_cache = std::unordered_set<PyObject const*>; using added_result_cache = std::unordered_map<PyObject const*, result_index_t>; using added_custom_operation_cache = std::unordered_map<PyObject const*, std::size_t>; @@ -172,12 +185,13 @@ private: return delay_index; } - void add_source(py::handle op, std::size_t input_index, std::string_view prefix, sfg_info_stack const& sfg_stack) { + void add_source(py::handle op, std::size_t input_index, std::string_view prefix, sfg_info_stack const& sfg_stack, + delay_queue& deferred_delays) { auto const signal = py::object{op.attr("inputs")[py::int_{input_index}].attr("signals")[py::int_{0}]}; auto const src = py::handle{signal.attr("source")}; auto const operation = py::handle{src.attr("operation")}; auto const index = src.attr("index").cast<std::size_t>(); - this->add_operation_output(operation, index, prefix, sfg_stack); + this->add_operation_output(operation, index, prefix, sfg_stack, deferred_delays); if (!signal.attr("bits").is_none()) { auto const bits = signal.attr("bits").cast<std::size_t>(); if (bits > 64) { @@ -189,54 +203,56 @@ private: } void add_unary_operation_output(py::handle op, result_index_t result_index, std::string_view prefix, sfg_info_stack const& sfg_stack, - instruction_type type) { - this->add_source(op, 0, prefix, sfg_stack); + delay_queue& deferred_delays, instruction_type type) { + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); this->add_instruction(type, result_index, 0); } void add_binary_operation_output(py::handle op, result_index_t result_index, std::string_view prefix, sfg_info_stack const& sfg_stack, - instruction_type type) { - this->add_source(op, 0, prefix, sfg_stack); - this->add_source(op, 1, prefix, sfg_stack); + delay_queue& deferred_delays, instruction_type type) { + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); + this->add_source(op, 1, prefix, sfg_stack, deferred_delays); this->add_instruction(type, result_index, -1); } - void add_operation_output(py::handle op, std::size_t output_index, std::string_view prefix, sfg_info_stack const& sfg_stack) { + void add_operation_output(py::handle op, std::size_t output_index, std::string_view prefix, sfg_info_stack const& sfg_stack, + delay_queue& deferred_delays) { auto const type_name = op.attr("type_name").cast<std::string_view>(); if (type_name == "out") { - this->add_source(op, 0, prefix, sfg_stack); + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); } else if (auto const result_index = this->begin_operation_output(op, output_index, prefix)) { if (type_name == "c") { this->add_instruction(instruction_type::push_constant, *result_index, 1).value = op.attr("value").cast<number>(); } else if (type_name == "add") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::addition); + this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::addition); } else if (type_name == "sub") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::subtraction); + this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::subtraction); } else if (type_name == "mul") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::multiplication); + this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::multiplication); } else if (type_name == "div") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::division); + this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::division); } else if (type_name == "min") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::min); + this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::min); } else if (type_name == "max") { - this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::max); + this->add_binary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::max); } else if (type_name == "sqrt") { - this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::square_root); + this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::square_root); } else if (type_name == "conj") { - this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::complex_conjugate); + this->add_unary_operation_output( + op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::complex_conjugate); } else if (type_name == "abs") { - this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, instruction_type::absolute); + this->add_unary_operation_output(op, *result_index, prefix, sfg_stack, deferred_delays, instruction_type::absolute); } else if (type_name == "cmul") { - this->add_source(op, 0, prefix, sfg_stack); + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); this->add_instruction(instruction_type::constant_multiplication, *result_index, 0).value = op.attr("value").cast<number>(); } else if (type_name == "bfly") { if (output_index == 0) { - this->add_source(op, 0, prefix, sfg_stack); - this->add_source(op, 1, prefix, sfg_stack); + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); + this->add_source(op, 1, prefix, sfg_stack, deferred_delays); this->add_instruction(instruction_type::addition, *result_index, -1); } else { - this->add_source(op, 0, prefix, sfg_stack); - this->add_source(op, 1, prefix, sfg_stack); + this->add_source(op, 0, prefix, sfg_stack, deferred_delays); + this->add_source(op, 1, prefix, sfg_stack, deferred_delays); this->add_instruction(instruction_type::subtraction, *result_index, -1); } } else if (type_name == "in") { @@ -248,22 +264,25 @@ private: if (sfg_stack.size() == 1) { this->add_instruction(instruction_type::push_input, *result_index, 1).index = input_index; } else { - this->add_source(info.sfg, input_index, prefix.substr(0, info.prefix_length), pop_sfg(sfg_stack)); + this->add_source(info.sfg, input_index, prefix.substr(0, info.prefix_length), pop_sfg(sfg_stack), deferred_delays); this->add_instruction(instruction_type::forward_value, *result_index, 0); } } else if (type_name == "t") { auto const delay_index = this->add_delay_info(op.attr("initial_value").cast<number>(), *result_index); - this->add_source(op, 0, prefix, sfg_stack); - this->add_instruction(instruction_type::delay, *result_index, 0).index = delay_index; + deferred_delays.emplace_back(delay_index, op, std::string{prefix}, sfg_stack); + this->add_instruction(instruction_type::push_delay, *result_index, 1).index = delay_index; } else if (type_name == "sfg") { - this->add_source( - op.attr("output_operations")[py::int_{output_index}], 0, key_base(op, prefix), push_sfg(sfg_stack, op, prefix.size())); + this->add_source(op.attr("output_operations")[py::int_{output_index}], + 0, + key_base(op, prefix), + push_sfg(sfg_stack, op, prefix.size()), + deferred_delays); this->add_instruction(instruction_type::forward_value, *result_index, 0); } else { auto const custom_operation_index = this->try_add_custom_operation(op); auto const& custom_operation = m_code.custom_operations[custom_operation_index]; for (auto const i : range(custom_operation.input_count)) { - this->add_source(op, i, prefix, sfg_stack); + this->add_source(op, i, prefix, sfg_stack, deferred_delays); } auto const custom_source_index = m_code.custom_sources.size(); auto& custom_source = m_code.custom_sources.emplace_back(); diff --git a/src/simulation/format_code.h b/src/simulation/format_code.h index f50a3b7771c4a1da1eca5ff480c3fab8176dc370..5ebbb95d1f11eb18b915dbab9fbccbb82d83304c 100644 --- a/src/simulation/format_code.h +++ b/src/simulation/format_code.h @@ -12,7 +12,7 @@ namespace asic { -[[maybe_unused]] [[nodiscard]] inline std::string format_number(number const& value) { +[[nodiscard]] inline std::string format_number(number const& value) { if (value.imag() == 0) { return fmt::to_string(value.real()); } @@ -25,7 +25,7 @@ namespace asic { return fmt::format("{}+{}j", value.real(), value.imag()); } -[[maybe_unused]] [[nodiscard]] inline std::string format_compiled_simulation_code_result_keys(simulation_code const& code) { +[[nodiscard]] inline std::string format_compiled_simulation_code_result_keys(simulation_code const& code) { auto result = std::string{}; for (auto const& [i, result_key] : enumerate(code.result_keys)) { result += fmt::format("{:>2}: \"{}\"\n", i, result_key); @@ -33,7 +33,7 @@ namespace asic { return result; } -[[maybe_unused]] [[nodiscard]] inline std::string format_compiled_simulation_code_delays(simulation_code const& code) { +[[nodiscard]] inline std::string format_compiled_simulation_code_delays(simulation_code const& code) { auto result = std::string{}; for (auto const& [i, delay] : enumerate(code.delays)) { ASIC_ASSERT(delay.result_index < code.result_keys.size()); @@ -46,11 +46,12 @@ namespace asic { return result; } -[[maybe_unused]] [[nodiscard]] inline std::string format_compiled_simulation_code_instruction(instruction const& instruction) { +[[nodiscard]] inline std::string format_compiled_simulation_code_instruction(instruction const& instruction) { switch (instruction.type) { // clang-format off case instruction_type::push_input: return fmt::format("push_input inputs[{}]", instruction.index); case instruction_type::push_result: return fmt::format("push_result results[{}]", instruction.index); + case instruction_type::push_delay: return fmt::format("push_delay delays[{}]", instruction.index); case instruction_type::push_constant: return fmt::format("push_constant {}", format_number(instruction.value)); case instruction_type::truncate: return fmt::format("truncate {:#018x}", instruction.bit_mask); case instruction_type::addition: return "addition"; @@ -63,15 +64,15 @@ namespace asic { case instruction_type::complex_conjugate: return "complex_conjugate"; case instruction_type::absolute: return "absolute"; case instruction_type::constant_multiplication: return fmt::format("constant_multiplication {}", format_number(instruction.value)); - case instruction_type::delay: return fmt::format("delay delays[{}]", instruction.index); + case instruction_type::update_delay: return fmt::format("update_delay delays[{}]", instruction.index); case instruction_type::custom: return fmt::format("custom custom_sources[{}]", instruction.index); case instruction_type::forward_value: return "forward_value"; - // clang-format on + // clang-format on } return std::string{}; } -[[maybe_unused]] [[nodiscard]] inline std::string format_compiled_simulation_code_instructions(simulation_code const& code) { +[[nodiscard]] inline std::string format_compiled_simulation_code_instructions(simulation_code const& code) { auto result = std::string{}; for (auto const& [i, instruction] : enumerate(code.instructions)) { auto instruction_string = format_compiled_simulation_code_instruction(instruction); @@ -84,7 +85,7 @@ namespace asic { return result; } -[[maybe_unused]] [[nodiscard]] inline std::string format_compiled_simulation_code(simulation_code const& code) { +[[nodiscard]] inline std::string format_compiled_simulation_code(simulation_code const& code) { return fmt::format( "==============================================\n" "> Code stats\n" diff --git a/src/simulation/instruction.h b/src/simulation/instruction.h index 5fd95f92fb42ad54e1c6cf1c8d07e662129cdd6c..d650c651394a243c52eee7e5ad2fe463f96bdad7 100644 --- a/src/simulation/instruction.h +++ b/src/simulation/instruction.h @@ -12,6 +12,7 @@ namespace asic { enum class instruction_type : std::uint8_t { push_input, // push(inputs[index]) push_result, // push(results[index]) + push_delay, // push(delays[index]) push_constant, // push(value) truncate, // push(trunc(pop(), bit_mask)) addition, // push(pop() + pop()) @@ -24,7 +25,7 @@ enum class instruction_type : std::uint8_t { complex_conjugate, // push(conj(pop())) absolute, // push(abs(pop())) constant_multiplication, // push(pop() * value) - delay, // auto const value = pop(); push(delays[index]); delays[index] = value + update_delay, // delays[index] = pop() custom, // Custom operation. Uses custom_source[index]. forward_value // Forward the current value on the stack (push(pop()), i.e. do nothing). }; diff --git a/src/simulation/run.cpp b/src/simulation/run.cpp index 7120f8e42b06de161d5dc81e38c8c08eb9457a5b..4fa0d6ad075621762d4ef4b780071025b6ec6b98 100644 --- a/src/simulation/run.cpp +++ b/src/simulation/run.cpp @@ -85,6 +85,9 @@ simulation_state run_simulation(simulation_code const& code, span<number const> case instruction_type::push_result: push(state.results[instruction.index]); break; + case instruction_type::push_delay: + push(delays[instruction.index]); + break; case instruction_type::push_constant: push(instruction.value); break; @@ -135,12 +138,9 @@ simulation_state run_simulation(simulation_code const& code, span<number const> case instruction_type::constant_multiplication: push(pop() * instruction.value); break; - case instruction_type::delay: { - auto const value = delays[instruction.index]; + case instruction_type::update_delay: delays[instruction.index] = pop(); - push(value); break; - } case instruction_type::custom: { using namespace pybind11::literals; auto const& src = code.custom_sources[instruction.index]; diff --git a/test/fixtures/signal_flow_graph.py b/test/fixtures/signal_flow_graph.py index 1424d5ca2e9150fbb4365204a591f7a7764d2328..959fd944db5ddc12eba5fba3019bee9b2b8eb254 100644 --- a/test/fixtures/signal_flow_graph.py +++ b/test/fixtures/signal_flow_graph.py @@ -98,13 +98,33 @@ def sfg_accumulator(): data_out = Output(t) return SFG(inputs = [data_in, reset], outputs = [data_out]) + +@pytest.fixture +def sfg_simple_accumulator(): + """Valid SFG with two inputs and one output. + . . + in1----->add1-----+----->out1 + . ^ | . + . | | . + . +--t1<--+ . + . . + """ + in1 = Input() + t1 = Delay() + add1 = in1 + t1 + t1 << add1 + out1 = Output(add1) + return SFG(inputs = [in1], outputs = [out1]) + @pytest.fixture def sfg_simple_filter(): """A valid SFG that is used as a filter in the first lab for TSTE87. - +----<cmul1----+ - | | - | | - in1>------add1>----t1>-----+------out1> + . . + . +--cmul1<--+ . + . | | . + . v | . + in1---->add1----->t1+---->out1 + . . """ in1 = Input() t1 = Delay() diff --git a/test/test_fast_simulation.py b/test/test_fast_simulation.py index 7ab490362cc6dc246c0d1ab1c62cd34a4bc1ee8a..ece9add5891cb47cba7b63f80682bc65d970470e 100644 --- a/test/test_fast_simulation.py +++ b/test/test_fast_simulation.py @@ -179,6 +179,12 @@ class TestRun: assert output4[0] == 0 assert output5[0] == 7 + def test_simple_accumulator(self, sfg_simple_accumulator): + data_in = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + simulation = FastSimulation(sfg_simple_accumulator, [data_in]) + simulation.run() + assert list(simulation.results["0"]) == [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] + def test_simple_filter(self, sfg_simple_filter): input0 = np.array([1, 2, 3, 4, 5]) simulation = FastSimulation(sfg_simple_filter, [input0]) diff --git a/test/test_simulation.py b/test/test_simulation.py index e5f8a76e005d12acd28a67969539313e1bafd783..e1075a74a2c839eefcfa122ff02ec6d11314b11e 100644 --- a/test/test_simulation.py +++ b/test/test_simulation.py @@ -1,7 +1,7 @@ import pytest import numpy as np -from b_asic import SFG, Output, Simulation +from b_asic import SFG, Simulation class TestRunFor: @@ -176,6 +176,12 @@ class TestRun: assert output3[0] == 28 assert output4[0] == 0 assert output5[0] == 7 + + def test_simple_accumulator(self, sfg_simple_accumulator): + data_in = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + simulation = Simulation(sfg_simple_accumulator, [data_in]) + simulation.run() + assert list(simulation.results["0"]) == [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] def test_simple_filter(self, sfg_simple_filter): input0 = np.array([1, 2, 3, 4, 5])