diff --git a/include/hal_core/netlist/decorators/netlist_traversal_decorator.h b/include/hal_core/netlist/decorators/netlist_traversal_decorator.h index 7b6a174eae4..4a64fea7b95 100644 --- a/include/hal_core/netlist/decorators/netlist_traversal_decorator.h +++ b/include/hal_core/netlist/decorators/netlist_traversal_decorator.h @@ -186,8 +186,6 @@ namespace hal Result> get_next_sequential_gates(const Gate* gate, bool successors, const std::set& forbidden_pins, std::unordered_map>* cache = nullptr) const; - // TODO implement get_next_combinational_gates (get all combinational successor gates until sequential (non-combinational) gates are hit) - /** * Get the next sequential gates for all sequential gates in the netlist by traversing through remaining logic (e.g., combinational logic). * Compute a map from a sequential gate to all its successors. @@ -199,6 +197,32 @@ namespace hal */ Result>> get_next_sequential_gates_map(bool successors, const std::set& forbidden_pins) const; + /** + * Starting from the given net, traverse the netlist and return all combinational successor/predecessor gates. + * Continue traversal as long as further combinational gates are found and stop at gates that are not combinational. + * All combinational gates found during traversal are added to the result. + * Provide a cache to speed up traversal when calling this function multiple times on the same netlist. + * + * @param[in] net - Start net. + * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. + * @param[inout] cache - An optional cache that can be used for better performance on repeated calls. Defaults to a `nullptr`. + * @returns The next combinational gates on success, an error otherwise. + */ + Result> get_next_combinational_gates(const Net* net, bool successors, std::unordered_map>* cache = nullptr) const; + + /** + * Starting from the given gate, traverse the netlist and return all combinational successor/predecessor gates. + * Continue traversal as long as further combinational gates are found and stop at gates that are not combinational. + * All combinational gates found during traversal are added to the result. + * Provide a cache to speed up traversal when calling this function multiple times on the same netlist. + * + * @param[in] gate - Start gate. + * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. + * @param[inout] cache - An optional cache that can be used for better performance on repeated calls. Defaults to a `nullptr`. + * @returns The next combinational gates on success, an error otherwise. + */ + Result> get_next_combinational_gates(const Gate* gate, bool successors, std::unordered_map>* cache = nullptr) const; + // TODO move get_path and get_shortest_path here // TODO move get_gate_chain and get_complex_gate_chain here diff --git a/src/netlist/decorators/netlist_traversal_decorator.cpp b/src/netlist/decorators/netlist_traversal_decorator.cpp index 8c3bc44c909..3c1d36a647a 100644 --- a/src/netlist/decorators/netlist_traversal_decorator.cpp +++ b/src/netlist/decorators/netlist_traversal_decorator.cpp @@ -481,4 +481,122 @@ namespace hal return OK(std::move(seq_gate_map)); } + + Result> NetlistTraversalDecorator::get_next_combinational_gates(const Net* net, bool successors, std::unordered_map>* cache) 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); + + if (cache) + { + if (const auto it = cache->find(current); it != cache->end()) + { + const auto& cached_gates = std::get<1>(*it); + + // append cached gates to result + res.insert(cached_gates.begin(), cached_gates.end()); + + // pop net from stack as it has been dealt with + stack.pop_back(); + + continue; + } + } + + bool added = false; + for (const auto* entry_ep : successors ? current->get_destinations() : current->get_sources()) + { + auto* gate = entry_ep->get_gate(); + if (!gate->get_type()->has_property(GateTypeProperty::combinational)) + { + // stop traversal if not combinational + continue; + } + + // add to result if gate is combinational + res.insert(gate); + + // update cache + if (cache) + { + (*cache)[current].insert(gate); + for (const auto* n : previous) + { + (*cache)[n].insert(gate); + } + } + + for (const auto* exit_ep : successors ? gate->get_fan_out_endpoints() : gate->get_fan_in_endpoints()) + { + 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_combinational_gates(const Gate* gate, bool successors, std::unordered_map>* cache) 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()) + { + const auto next_res = this->get_next_combinational_gates(exit_ep->get_net(), successors, cache); + 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 index c52ae420f2b..e9548ee9cdc 100644 --- a/src/python_bindings/bindings/netlist_traversal_decorator.cpp +++ b/src/python_bindings/bindings/netlist_traversal_decorator.cpp @@ -391,5 +391,119 @@ namespace hal :returns: A dict from each sequential gate to all its sequential successors on success, ``None`` otherwise. :rtype: dict[hal_py.Gate,set[hal_py.Gate]] or None )"); + + py_netlist_traversal_decorator.def( + "get_next_combinational_gates", + [](NetlistTraversalDecorator& self, const Net* net, bool successors) -> std::optional> { + auto res = self.get_next_combinational_gates(net, successors, nullptr); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next combinational gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("net"), + py::arg("successors"), + R"( + Starting from the given net, traverse the netlist and return all combinational successor/predecessor gates. + Continue traversal as long as further combinational gates are found and stop at gates that are not combinational. + All combinational gates found during traversal are added to the result. + + :param hal_py.Net net: Start net. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :returns: The next combinational gates on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + + py_netlist_traversal_decorator.def( + "get_next_combinational_gates", + [](NetlistTraversalDecorator& self, const Net* net, bool successors, std::unordered_map>* cache) -> std::optional> { + auto res = self.get_next_combinational_gates(net, successors, cache); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next combinational gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("net"), + py::arg("successors"), + py::arg("cache"), + R"( + Starting from the given net, traverse the netlist and return all combinational successor/predecessor gates. + Continue traversal as long as further combinational gates are found and stop at gates that are not combinational. + All combinational gates found during traversal are added to the result. + Provide a cache to speed up traversal when calling this function multiple times on the same netlist. + + :param hal_py.Net net: Start net. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :param dict[hal_py.Net, set[hal_py.Gate]] cache: A cache that can be used for better performance on repeated calls. + :returns: The next combinational gates on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + + py_netlist_traversal_decorator.def( + "get_next_combinational_gates", + [](NetlistTraversalDecorator& self, const Gate* gate, bool successors) -> std::optional> { + auto res = self.get_next_combinational_gates(gate, successors, nullptr); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next combinational gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("gate"), + py::arg("successors"), + R"( + Starting from the given gate, traverse the netlist and return all combinational successor/predecessor gates. + Continue traversal as long as further combinational gates are found and stop at gates that are not combinational. + All combinational gates found during traversal are added to the result. + + :param hal_py.Gate gate: Start gate. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :returns: The next combinational gates on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); + + py_netlist_traversal_decorator.def( + "get_next_combinational_gates", + [](NetlistTraversalDecorator& self, const Gate* gate, bool successors, std::unordered_map>* cache) -> std::optional> { + auto res = self.get_next_combinational_gates(gate, successors, cache); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "error encountered while getting next combinational gates:\n{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("gate"), + py::arg("successors"), + py::arg("cache"), + R"( + Starting from the given gate, traverse the netlist and return all combinational successor/predecessor gates. + Continue traversal as long as further combinational gates are found and stop at gates that are not combinational. + All combinational gates found during traversal are added to the result. + Provide a cache to speed up traversal when calling this function multiple times on the same netlist. + + :param hal_py.Gate gate: Start gate. + :param bool successors: Set ``True`` to get successors, set ``False`` to get predecessors. + :param dict[hal_py.Net, set[hal_py.Gate]] cache: A cache that can be used for better performance on repeated calls. + :returns: The next combinational gates on success, ``None`` otherwise. + :rtype: set[hal_py.Gate] or None + )"); } } // namespace hal \ No newline at end of file