diff --git a/include/hal_core/netlist/decorators/netlist_traversal_decorator.h b/include/hal_core/netlist/decorators/netlist_traversal_decorator.h new file mode 100644 index 00000000000..33dd603b197 --- /dev/null +++ b/include/hal_core/netlist/decorators/netlist_traversal_decorator.h @@ -0,0 +1,139 @@ +// MIT License +// +// Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved. +// Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved. +// Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "hal_core/defines.h" +#include "hal_core/netlist/netlist.h" +#include "hal_core/utilities/result.h" + +namespace hal +{ + /** + * A netlist decorator that provides functionality to traverse the associated netlist without making any modifications. + * + * @ingroup decorators + */ + class NETLIST_API NetlistTraversalDecorator + { + public: + /** + * Construct new NetlistTraversalDecorator object. + * + * @param[in] netlist - The netlist to operate on. + */ + NetlistTraversalDecorator(const Netlist& netlist); + + /** + * Starting from the given net, traverse the netlist and return only the successor/predecessor gates for which the `target_gate_filter` evaluates to `true`. + * Traverse over gates that do not meet the `target_gate_filter` condition. + * Stop traversal if (1) the `target_gate_filter` evaluates to `true`, (2) the `exit_endpoint_filter` evaluates to `false` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal), or (3) the `entry_endpoint_filter` evaluates to `false` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + * Both the `entry_endpoint_filter` and the `exit_endpoint_filter` may be omitted. + * + * @param[in] net - Start net. + * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. + * @param[in] target_gate_filter - Filter condition that must be met for the target gates. + * @param[in] exit_endpoint_filter - Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + * @param[in] entry_endpoint_filter - Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + * @returns The next gates fulfilling the target gate filter condition. + */ + Result> get_next_matching_gates(const Net* net, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) const; + + /** + * Starting from the given gate, traverse the netlist and return only the successor/predecessor gates for which the `target_gate_filter` evaluates to `true`. + * Traverse over gates that do not meet the `target_gate_filter` condition. + * Stop traversal if (1) the `target_gate_filter` evaluates to `true`, (2) the `exit_endpoint_filter` evaluates to `false` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal), or (3) the `entry_endpoint_filter` evaluates to `false` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + * Both the `entry_endpoint_filter` and the `exit_endpoint_filter` may be omitted. + * + * @param[in] gate - Start gate. + * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. + * @param[in] target_gate_filter - Filter condition that must be met for the target gates. + * @param[in] exit_endpoint_filter - Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + * @param[in] entry_endpoint_filter - Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + * @returns The next gates fulfilling the target gate filter condition. + */ + Result> get_next_matching_gates(const Gate* gate, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) const; + + /** + * Starting from the given net, traverse the netlist and return only the successor/predecessor gates for which the `target_gate_filter` evaluates to `true`. + * Continues traversal independent of whatever `target_gate_filter` evaluates to. + * Stop traversal if (1) the `exit_endpoint_filter` evaluates to `false` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal) or (2) the `entry_endpoint_filter` evaluates to `false` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + * The target_gate_filter may be omitted in which case all traversed gates will be returned. + * Both `entry_endpoint_filter` and the `exit_endpoint_filter` may be omitted as well. + * + * @param[in] net - Start net. + * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. + * @param[in] target_gate_filter - Filter condition that must be met for the target gates. + * @param[in] exit_endpoint_filter - Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + * @param[in] entry_endpoint_filter - Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + * @returns The next gates fulfilling the target gate filter condition. + */ + Result> get_next_matching_gates_until(const Net* net, + bool successors, + const std::function& target_gate_filter = nullptr, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) const; + + /** + * Starting from the given gate, traverse the netlist and return only the successor/predecessor gates for which the `target_gate_filter` evaluates to `true`. + * Stop traversal if (1) the `exit_endpoint_filter` evaluates to `false` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal) or (2) the `entry_endpoint_filter` evaluates to `false` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + * The target_gate_filter may be omitted in which case all traversed gates will be returned. + * Both `entry_endpoint_filter` and the `exit_endpoint_filter` may be omitted as well. + * + * @param[in] gate - Start gate. + * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. + * @param[in] target_gate_filter - Filter condition that must be met for the target gates. + * @param[in] exit_endpoint_filter - Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + * @param[in] entry_endpoint_filter - Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + * @returns The next gates fulfilling the target gate filter condition. + */ + Result> get_next_matching_gates_until(const Gate* gate, + bool successors, + const std::function& target_gate_filter = nullptr, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) const; + + // TODO implement get_next_sequential_gates (get all sequential successors of the current gate by traversing other gates) + + // TODO implement get_next_combinational_gates (get all combinational successor gates until sequential (non-combinational) gates are hit) + + // TODO implement get_sequential_successor_map (iteratively call get_next_sequential_gates on all sequential gates and create a map) + + // TODO move get_path and get_shortest_path here + + // TODO move get_gate_chain and get_complex_gate_chain here + + private: + const Netlist& m_netlist; + }; +} // namespace hal \ No newline at end of file diff --git a/include/hal_core/python_bindings/python_bindings.h b/include/hal_core/python_bindings/python_bindings.h index bebfee4fb82..6523e0763e2 100644 --- a/include/hal_core/python_bindings/python_bindings.h +++ b/include/hal_core/python_bindings/python_bindings.h @@ -33,8 +33,9 @@ #include "hal_core/netlist/boolean_function/types.h" #include "hal_core/netlist/decorators/boolean_function_decorator.h" #include "hal_core/netlist/decorators/boolean_function_net_decorator.h" -#include "hal_core/netlist/decorators/subgraph_netlist_decorator.h" #include "hal_core/netlist/decorators/netlist_modification_decorator.h" +#include "hal_core/netlist/decorators/netlist_traversal_decorator.h" +#include "hal_core/netlist/decorators/subgraph_netlist_decorator.h" #include "hal_core/netlist/gate.h" #include "hal_core/netlist/gate_library/enums/async_set_reset_behavior.h" #include "hal_core/netlist/gate_library/gate_library.h" @@ -319,6 +320,13 @@ namespace hal */ void netlist_modification_decorator_init(py::module& m); + /** + * Initializes Python bindings for the HAL netlist traversal decorator in a python module. + * + * @param[in] m - the python module + */ + void netlist_traversal_decorator_init(py::module& m); + /** * Initializes Python bindings for the HAL LogManager in a python module. * diff --git a/src/netlist/decorators/netlist_traversal_decorator.cpp b/src/netlist/decorators/netlist_traversal_decorator.cpp new file mode 100644 index 00000000000..70f9e1f1c2a --- /dev/null +++ b/src/netlist/decorators/netlist_traversal_decorator.cpp @@ -0,0 +1,237 @@ +#include "hal_core/netlist/decorators/netlist_traversal_decorator.h" + +#include "hal_core/netlist/gate.h" +#include "hal_core/netlist/net.h" + +namespace hal +{ + NetlistTraversalDecorator::NetlistTraversalDecorator(const Netlist& netlist) : m_netlist(netlist) + { + } + + Result> NetlistTraversalDecorator::get_next_matching_gates(const Net* net, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter, + const std::function& entry_endpoint_filter) const + { + if (net == nullptr) + { + return ERR("nullptr given as net"); + } + + if (!m_netlist.is_net_in_netlist(net)) + { + return ERR("net does not belong to netlist"); + } + + std::unordered_set visited; + std::vector stack = {net}; + std::vector previous; + std::set res; + while (!stack.empty()) + { + const Net* current = stack.back(); + + if (!previous.empty() && current == previous.back()) + { + stack.pop_back(); + previous.pop_back(); + continue; + } + + visited.insert(current); + + bool added = false; + for (const auto* entry_ep : successors ? current->get_destinations() : current->get_sources()) + { + if (entry_endpoint_filter != nullptr && !entry_endpoint_filter(entry_ep, previous.size() + 1)) + { + continue; + } + + auto* g = entry_ep->get_gate(); + + if (target_gate_filter(g)) + { + res.insert(g); + } + else + { + for (const auto* exit_ep : successors ? g->get_fan_out_endpoints() : g->get_fan_in_endpoints()) + { + if (exit_endpoint_filter != nullptr && !exit_endpoint_filter(exit_ep, previous.size() + 1)) + { + continue; + } + + const Net* n = exit_ep->get_net(); + if (visited.find(n) == visited.end()) + { + stack.push_back(n); + added = true; + } + } + } + } + + if (added) + { + previous.push_back(current); + } + else + { + stack.pop_back(); + } + } + + return OK(res); + } + + Result> NetlistTraversalDecorator::get_next_matching_gates(const Gate* gate, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter, + const std::function& entry_endpoint_filter) const + { + if (gate == nullptr) + { + return ERR("nullptr given as gate"); + } + + if (!m_netlist.is_gate_in_netlist(gate)) + { + return ERR("net does not belong to netlist"); + } + + std::set res; + for (const auto* exit_ep : successors ? gate->get_fan_out_endpoints() : gate->get_fan_in_endpoints()) + { + if (exit_endpoint_filter != nullptr && !exit_endpoint_filter(exit_ep, 0)) + { + continue; + } + + const auto next_res = this->get_next_matching_gates(exit_ep->get_net(), successors, target_gate_filter, exit_endpoint_filter, entry_endpoint_filter); + if (next_res.is_error()) + { + return ERR(next_res.get_error()); + } + auto next = next_res.get(); + res.insert(next.begin(), next.end()); + } + return OK(res); + } + + Result> NetlistTraversalDecorator::get_next_matching_gates_until(const Net* net, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter, + const std::function& entry_endpoint_filter) const + { + if (net == nullptr) + { + return ERR("nullptr given as net"); + } + + if (!m_netlist.is_net_in_netlist(net)) + { + return ERR("net does not belong to netlist"); + } + + std::unordered_set visited; + std::vector stack = {net}; + std::vector previous; + std::set res; + while (!stack.empty()) + { + const Net* current = stack.back(); + + if (!previous.empty() && current == previous.back()) + { + stack.pop_back(); + previous.pop_back(); + continue; + } + + visited.insert(current); + + bool added = false; + for (const auto* entry_ep : successors ? current->get_destinations() : current->get_sources()) + { + if (entry_endpoint_filter != nullptr && !entry_endpoint_filter(entry_ep, previous.size() + 1)) + { + continue; + } + + auto* g = entry_ep->get_gate(); + + if ((target_gate_filter == nullptr) || target_gate_filter(g)) + { + res.insert(g); + } + + for (const auto* exit_ep : successors ? g->get_fan_out_endpoints() : g->get_fan_in_endpoints()) + { + if (exit_endpoint_filter != nullptr && !exit_endpoint_filter(exit_ep, previous.size() + 1)) + { + continue; + } + + const Net* n = exit_ep->get_net(); + if (visited.find(n) == visited.end()) + { + stack.push_back(n); + added = true; + } + } + } + + if (added) + { + previous.push_back(current); + } + else + { + stack.pop_back(); + } + } + + return OK(res); + } + + Result> NetlistTraversalDecorator::get_next_matching_gates_until(const Gate* gate, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter, + const std::function& entry_endpoint_filter) const + { + if (gate == nullptr) + { + return ERR("nullptr given as gate"); + } + + if (!m_netlist.is_gate_in_netlist(gate)) + { + return ERR("net does not belong to netlist"); + } + + std::set res; + for (const auto* exit_ep : successors ? gate->get_fan_out_endpoints() : gate->get_fan_in_endpoints()) + { + if (exit_endpoint_filter != nullptr && !exit_endpoint_filter(exit_ep, 0)) + { + continue; + } + + const auto next_res = this->get_next_matching_gates_until(exit_ep->get_net(), successors, target_gate_filter, exit_endpoint_filter, entry_endpoint_filter); + if (next_res.is_error()) + { + return ERR(next_res.get_error()); + } + auto next = next_res.get(); + res.insert(next.begin(), next.end()); + } + return OK(res); + } +} // namespace hal \ No newline at end of file diff --git a/src/python_bindings/bindings/netlist_traversal_decorator.cpp b/src/python_bindings/bindings/netlist_traversal_decorator.cpp new file mode 100644 index 00000000000..d0054cc19e7 --- /dev/null +++ b/src/python_bindings/bindings/netlist_traversal_decorator.cpp @@ -0,0 +1,173 @@ +#include "hal_core/python_bindings/python_bindings.h" + +namespace hal +{ + void netlist_traversal_decorator_init(py::module& m) + { + py::class_ py_netlist_traversal_decorator( + m, "NetlistTraversalDecorator", R"(A netlist decorator that provides functionality to traverse the associated netlist without making any modifications.)"); + + py_netlist_traversal_decorator.def(py::init(), py::arg("netlist"), R"( + Construct new NetlistTraversalDecorator object. + + :param hal_py.Netlist netlist: The netlist to operate on. + )"); + + py_netlist_traversal_decorator.def( + "get_next_matching_gates", + [](NetlistTraversalDecorator& self, + const Net* net, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) -> std::optional> { + auto res = self.get_next_matching_gates(net, successors, target_gate_filter, exit_endpoint_filter, entry_endpoint_filter); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("net"), + py::arg("successors"), + py::arg("target_gate_filter"), + py::arg("exit_endpoint_filter") = nullptr, + py::arg("entry_endpoint_filter") = nullptr, + R"( + Starting from the given net, traverse the netlist and return only the successor/predecessor gates for which the ``target_gate_filter`` evaluates to ``True``. + Traverse over gates that do not meet the ``target_gate_filter`` condition. + Stop traversal if (1) the ``target_gate_filter`` evaluates to ``True``, (2) the ``exit_endpoint_filter`` evaluates to ``False`` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal), or (3) the ``entry_endpoint_filter`` evaluates to ``False`` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + Both the ``entry_endpoint_filter`` and the ``exit_endpoint_filter`` may be omitted. + + :param hal_py.Net net: Start net. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :param lambda target_gate_filter: Filter condition that must be met for the target gates. + :param lambda exit_endpoint_filter: Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + :param lambda entry_endpoint_filter: Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + :returns: The next gates fulfilling the target gate filter condition on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + + py_netlist_traversal_decorator.def( + "get_next_matching_gates", + [](NetlistTraversalDecorator& self, + const Gate* gate, + bool successors, + const std::function& target_gate_filter, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) -> std::optional> { + auto res = self.get_next_matching_gates(gate, successors, target_gate_filter, exit_endpoint_filter, entry_endpoint_filter); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("gate"), + py::arg("successors"), + py::arg("target_gate_filter"), + py::arg("exit_endpoint_filter") = nullptr, + py::arg("entry_endpoint_filter") = nullptr, + R"( + Starting from the given gate, traverse the netlist and return only the successor/predecessor gates for which the ``target_gate_filter`` evaluates to ``True``. + Traverse over gates that do not meet the ``target_gate_filter`` condition. + Stop traversal if (1) the ``target_gate_filter`` evaluates to ``True``, (2) the ``exit_endpoint_filter`` evaluates to ``False`` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal), or (3) the ``entry_endpoint_filter`` evaluates to ``False`` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + Both the ``entry_endpoint_filter`` and the ``exit_endpoint_filter`` may be omitted. + + :param hal_py.Gate gate: Start gate. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :param lambda target_gate_filter: Filter condition that must be met for the target gates. + :param lambda exit_endpoint_filter: Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + :param lambda entry_endpoint_filter: Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + :returns: The next gates fulfilling the target gate filter condition on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + + py_netlist_traversal_decorator.def( + "get_next_matching_gates_until", + [](NetlistTraversalDecorator& self, + const Net* net, + bool successors, + const std::function& target_gate_filter = nullptr, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) -> std::optional> { + auto res = self.get_next_matching_gates_until(net, successors, target_gate_filter, exit_endpoint_filter, entry_endpoint_filter); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("net"), + py::arg("successors"), + py::arg("target_gate_filter"), + py::arg("exit_endpoint_filter") = nullptr, + py::arg("entry_endpoint_filter") = nullptr, + R"( + Starting from the given net, traverse the netlist and return only the successor/predecessor gates for which the ``target_gate_filter`` evaluates to ``True``. + Continues traversal independent of whatever ``target_gate_filter`` evaluates to. + Stop traversal if (1) the ``exit_endpoint_filter`` evaluates to ``False`` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal) or (2) the ``entry_endpoint_filter`` evaluates to ``False`` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + The target_gate_filter may be omitted in which case all traversed gates will be returned. + Both ``entry_endpoint_filter`` and the ``exit_endpoint_filter`` may be omitted as well. + + :param hal_py.Net net: Start net. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :param lambda target_gate_filter: Filter condition that must be met for the target gates. + :param lambda exit_endpoint_filter: Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + :param lambda entry_endpoint_filter: Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + :returns: The next gates fulfilling the target gate filter condition on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + + py_netlist_traversal_decorator.def( + "get_next_matching_gates_until", + [](NetlistTraversalDecorator& self, + const Gate* gate, + bool successors, + const std::function& target_gate_filter = nullptr, + const std::function& exit_endpoint_filter = nullptr, + const std::function& entry_endpoint_filter = nullptr) -> std::optional> { + auto res = self.get_next_matching_gates_until(gate, successors, target_gate_filter, exit_endpoint_filter, entry_endpoint_filter); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("gate"), + py::arg("successors"), + py::arg("target_gate_filter"), + py::arg("exit_endpoint_filter") = nullptr, + py::arg("entry_endpoint_filter") = nullptr, + R"( + Starting from the given gate, traverse the netlist and return only the successor/predecessor gates for which the ``target_gate_filter`` evaluates to ``True``. + Stop traversal if (1) the ``exit_endpoint_filter`` evaluates to ``False`` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal) or (2) the ``entry_endpoint_filter`` evaluates to ``False`` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). + The target_gate_filter may be omitted in which case all traversed gates will be returned. + Both ``entry_endpoint_filter`` and the ``exit_endpoint_filter`` may be omitted as well. + + :param hal_py.Gate gate: Start gate. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :param lambda target_gate_filter: Filter condition that must be met for the target gates. + :param lambda exit_endpoint_filter: Filter condition that determines whether to stop traversal on a fan-in/out endpoint. + :param lambda entry_endpoint_filter: Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + :returns: The next gates fulfilling the target gate filter condition on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + } +} // namespace hal \ No newline at end of file diff --git a/src/python_bindings/python_bindings.cpp b/src/python_bindings/python_bindings.cpp index 7e39d491c0a..09712db6a20 100644 --- a/src/python_bindings/python_bindings.cpp +++ b/src/python_bindings/python_bindings.cpp @@ -75,6 +75,8 @@ namespace hal netlist_modification_decorator_init(m); + netlist_traversal_decorator_init(m); + log_init(m); #ifndef PYBIND11_MODULE