From 5194e783b52af2ce634da5d35de1c921a6f186f3 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 10 Oct 2025 15:37:43 -0400 Subject: [PATCH 01/23] buggy and incomplete prototype for clock tree extraction --- plugins/clock_tree_extractor/CMakeLists.txt | 24 ++ .../clock_tree_extractor.h | 77 ++++++ .../plugin_clock_tree_extractor.h | 58 +++++ .../python/python_bindings.cc | 130 ++++++++++ .../src/clock_tree_extractor.cc | 225 ++++++++++++++++++ .../src/plugin_clock_tree_extractor.cc | 42 ++++ 6 files changed, 556 insertions(+) create mode 100644 plugins/clock_tree_extractor/CMakeLists.txt create mode 100644 plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h create mode 100644 plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h create mode 100644 plugins/clock_tree_extractor/python/python_bindings.cc create mode 100644 plugins/clock_tree_extractor/src/clock_tree_extractor.cc create mode 100644 plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cc diff --git a/plugins/clock_tree_extractor/CMakeLists.txt b/plugins/clock_tree_extractor/CMakeLists.txt new file mode 100644 index 000000000000..b234ac639a5d --- /dev/null +++ b/plugins/clock_tree_extractor/CMakeLists.txt @@ -0,0 +1,24 @@ +option(PL_CLOCK_TREE_EXTRACTOR "PL_CLOCK_TREE_EXTRACTOR" ON) + +if(PL_CLOCK_TREE_EXTRACTOR OR BUILD_ALL_PLUGINS) + + if(IWYU) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use") + message(STATUS "include-what-you-use turned ON") + else() + message(STATUS "include-what-you-use turned OFF") + endif() + + file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc) + file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cc) + + hal_add_plugin(clock_tree_extractor + SHARED + HEADER ${CLOCK_TREE_EXTRACTOR_INC} + SOURCES ${CLOCK_TREE_EXTRACTOR_SRC} ${CLOCK_TREE_EXTRACTOR_PYTHON_SRC} + LINK_LIBRARIES graph_algorithm + COMPILE_OPTIONS "-march=native" + ) + +endif() diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h new file mode 100644 index 000000000000..e458a1b46999 --- /dev/null +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h @@ -0,0 +1,77 @@ +// 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/utilities/result.h" + +#include + +namespace hal +{ + class Netlist; +} + +namespace hal +{ + class Gate; +} + +namespace hal +{ + class GateType; +} + +namespace hal +{ + class Endpoint; +} + +namespace hal +{ + namespace cte + { + class ClockTreeExtractor + { + public: + ClockTreeExtractor() = default; + + ClockTreeExtractor( const Netlist *netlist ); + + ~ClockTreeExtractor() = default; + + Result analyze( const std::string &pathname ); + + const std::vector> get_edges() const; + + private: + const Netlist *m_netlist; + + std::vector> m_edges; + }; + } // namespace cte +} // namespace hal diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h new file mode 100644 index 000000000000..f2f7bcabeff6 --- /dev/null +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h @@ -0,0 +1,58 @@ +// 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/plugin_system/plugin_interface_base.h" + +#include +#include + +namespace hal +{ + class PLUGIN_API ClockTreeExtractorPlugin : public BasePluginInterface + { + public: + ClockTreeExtractorPlugin() = default; + + ~ClockTreeExtractorPlugin() = default; + + std::string get_name() const override; + + std::string get_version() const override; + + std::string get_description() const override; + + std::set get_dependencies() const override; + + void initialize() override; + + void on_load() override; + + void on_unload() override; + }; +} // namespace hal diff --git a/plugins/clock_tree_extractor/python/python_bindings.cc b/plugins/clock_tree_extractor/python/python_bindings.cc new file mode 100644 index 000000000000..9b3a40fda8cc --- /dev/null +++ b/plugins/clock_tree_extractor/python/python_bindings.cc @@ -0,0 +1,130 @@ +#include "hal_core/python_bindings/python_bindings.h" + +#include "clock_tree_extractor/clock_tree_extractor.h" +#include "clock_tree_extractor/plugin_clock_tree_extractor.h" +#include "pybind11/pybind11.h" + +#include +#include +#include +#include +#include + +namespace hal +{ + class BasePluginInterface; +} +namespace hal +{ + class Netlist; +} + +namespace py = pybind11; + +namespace hal +{ + + // the name in PYBIND11_MODULE/PYBIND11_PLUGIN *MUST* match the filename of the output library (without extension), + // otherwise you will get "ImportError: dynamic module does not define module export function" when importing the + // module + +#ifdef PYBIND11_MODULE + PYBIND11_MODULE( clock_tree_extractor, m ) + { + m.doc() = ""; +#else + PYBIND11_PLUGIN( clock_tree_extractor ) + { + py::module m( "clock_tree_extractor", "" ); +#endif // ifdef PYBIND11_MODULE + + py::class_, BasePluginInterface> + py_clock_tree_extractor_plugin( m, "ClockTreeExtractorPlugin", "" ); + + py_clock_tree_extractor_plugin.def_property_readonly( "name", &ClockTreeExtractorPlugin::get_name, R"( + The name of the plugin. + + :type: str + )" ); + + py_clock_tree_extractor_plugin.def( "get_name", &ClockTreeExtractorPlugin::get_name, R"( + Get the name of the plugin. + + :returns: The name of the plugin. + :rtype: str + )" ); + + py_clock_tree_extractor_plugin.def_property_readonly( "version", &ClockTreeExtractorPlugin::get_version, R"( + The version of the plugin. + + :type: str + )" ); + + py_clock_tree_extractor_plugin.def( "get_version", &ClockTreeExtractorPlugin::get_version, R"( + Get the version of the plugin. + + :returns: The version of the plugin. + :rtype: str + )" ); + + py_clock_tree_extractor_plugin.def_property_readonly( + "description", &ClockTreeExtractorPlugin::get_description, R"( + The description of the plugin. + + :type: str + )" ); + + py_clock_tree_extractor_plugin.def( "get_description", &ClockTreeExtractorPlugin::get_description, R"( + Get the description of the plugin. + + :returns: The description of the plugin. + :rtype: str + )" ); + + py_clock_tree_extractor_plugin.def_property_readonly( + "dependencies", &ClockTreeExtractorPlugin::get_dependencies, R"( + A set of plugin names that this plugin depends on. + + :type: set[str] + )" ); + + py_clock_tree_extractor_plugin.def( "get_dependencies", &ClockTreeExtractorPlugin::get_dependencies, R"( + Get a set of plugin names that this plugin depends on. + + :returns: A set of plugin names that this plugin depends on. + :rtype: set[str] + )" ); + + py::class_> py_clock_tree_extractor( + m, "ClockTreeExtractor", R"( + + )" ); + + py_clock_tree_extractor.def( py::init(), py::arg( "netlist" ), R"( + + )" ); + + py_clock_tree_extractor.def( + "analyze", + []( cte::ClockTreeExtractor &self, const std::string &pathname ) -> std::optional { + auto res = self.analyze( pathname ); + if( res.is_ok() ) + { + return res.get(); + } + else + { + log_error( "python_context", "{}", res.get_error().get() ); + return std::nullopt; + } + }, + py::arg( "pathname" ), + R"( + + )" ); + +#ifndef PYBIND11_MODULE + return m.ptr(); +#endif // PYBIND11_MODULE + } +} // namespace hal \ No newline at end of file diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc new file mode 100644 index 000000000000..8f1d616b18c6 --- /dev/null +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -0,0 +1,225 @@ +#include "clock_tree_extractor/clock_tree_extractor.h" + +#include "hal_core/netlist/decorators/netlist_traversal_decorator.h" +#include "hal_core/netlist/endpoint.h" +#include "hal_core/netlist/gate.h" +#include "hal_core/netlist/net.h" +#include "hal_core/utilities/result.h" + +#include +#include + +namespace hal +{ + namespace cte + { + namespace + { + bool continue_through_exit_ep( const Endpoint *exit_ep, const u32 current_depth ) + { + if( exit_ep == nullptr ) + { + return false; + } + + const Gate *gate = exit_ep->get_gate(); + const GateType *gate_type = gate->get_type(); + const GateTypeProperty ff = GateTypeProperty::ff; + const GatePin *gate_pin = exit_ep->get_pin(); + + if( gate_type->has_property( ff ) && current_depth == 0 ) + { + return gate_pin->get_direction() == PinDirection::input && gate_pin->get_type() == PinType::clock; + } + + return !( gate_type->has_property( ff ) && current_depth > 0 ); + } + + void export_clock_tree( const std::unordered_map> &vertices, + const std::vector> edges, + const std::string &pathname ) + { + if( pathname == "" ) + { + return; + } + + std::ofstream dot_fd( pathname ); + + if( !dot_fd ) + { + log_error( "clock_tree_extractor", "could not export clock tree to '{}'", pathname ); + return; + } + + dot_fd << "digraph {\n"; + + if( vertices.find( "global_inputs" ) != vertices.end() ) + { + for( const std::string &net_name : vertices.at( "global_inputs" ) ) + { + dot_fd << " " << net_name << " [color=green];\n"; + } + } + + if( vertices.find( "ffs" ) != vertices.end() ) + { + for( const std::string &gate_id : vertices.at( "ffs" ) ) + { + dot_fd << " " << gate_id << " [color=dodgerblue];\n"; + } + } + + if( vertices.find( "buffers" ) != vertices.end() ) + { + for( const std::string &gate_id : vertices.at( "buffers" ) ) + { + dot_fd << " " << gate_id << " [color=gold];\n"; + } + } + + for( const auto &[src, dst] : edges ) + { + const std::string edge_color = + ( vertices.at( "buffers" ).find( src ) != vertices.at( "buffers" ).end() ) ? "gold" : "green"; + dot_fd << " " << src << " -> " << dst << " [color=" << edge_color << "];"; + } + + dot_fd << "}\n"; + dot_fd.close(); + + log_info( "clock_tree_extractor", "successfully exported clock tree to '{}'", pathname ); + } + } // namespace + + ClockTreeExtractor::ClockTreeExtractor( const Netlist *netlist ) + { + m_netlist = netlist; + } + + Result ClockTreeExtractor::analyze( const std::string &pathname = "" ) + { + if( m_netlist == nullptr ) + { + return ERR( "no netlist provided" ); + } + + std::queue queue; + std::unordered_set visited; + std::vector> edges; + std::unordered_map> vertices; + NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *m_netlist ); + + const std::vector start_gates = m_netlist->get_gates( + []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::ff ); } ); + + for( const Gate *gate : start_gates ) + { + queue.push( gate ); + const std::string gate_id = std::to_string( gate->get_id() ); + vertices["ffs"].insert( gate_id ); + } + + while( !queue.empty() ) + { + const Gate *current = queue.front(); + const std::string current_id = std::to_string( current->get_id() ); + queue.pop(); + + if( auto it = visited.find( current ); it != visited.end() ) + { + continue; + } + + visited.insert( current ); + + auto result = ntd.get_next_matching_gates( + current, + false, + []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::c_buffer ); }, + false, + continue_through_exit_ep, + nullptr ); + if( result.is_error() ) + { + return ERR( result.get_error() ); + } + const std::set next_matching_gates = result.get(); + + if( next_matching_gates.empty() ) + { + continue; + } + + for( const Gate *predecessor : next_matching_gates ) + { + const std::string predecessor_id = std::to_string( predecessor->get_id() ); + std::pair edge = std::make_pair( predecessor_id, current_id ); + if( std::find( edges.begin(), edges.end(), edge ) != edges.end() ) + { + continue; + } + edges.push_back( edge ); + queue.push( predecessor ); + vertices["buffers"].insert( predecessor_id ); + } + } + + for( const Net *net : m_netlist->get_global_input_nets() ) + { + const std::string net_name = net->get_name(); + + auto result = ntd.get_next_matching_gates( + net, + true, + []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::c_buffer ); }, + false, + continue_through_exit_ep, + nullptr ); + if( result.is_error() ) + { + return ERR( result.get_error() ); + } + const std::set next_matching_gates = result.get(); + + std::size_t childs = 0; + for( const Gate *gate : next_matching_gates ) + { + if( auto it = visited.find( gate ); it == visited.end() ) + { + continue; + } + + const u32 gate_id = gate->get_id(); + const std::string gate_id_str = std::to_string( gate_id ); + + const std::pair edge = std::make_pair( net_name, gate_id_str ); + + if( std::find( edges.begin(), edges.end(), edge ) != edges.end() ) + { + continue; + } + + edges.push_back( edge ); + childs++; + } + + if( childs > 0 ) + { + vertices["global_inputs"].insert( net_name ); + } + } + + export_clock_tree( vertices, edges, pathname ); + + m_edges = edges; + + return OK( edges.size() ); + } + + const std::vector> ClockTreeExtractor::get_edges() const + { + return m_edges; + } + } // namespace cte +} // namespace hal diff --git a/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cc new file mode 100644 index 000000000000..8e9a3e1b8783 --- /dev/null +++ b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cc @@ -0,0 +1,42 @@ +#include "clock_tree_extractor/plugin_clock_tree_extractor.h" + +namespace hal +{ + extern std::unique_ptr create_plugin_instance() + { + return std::make_unique(); + } + + std::string ClockTreeExtractorPlugin::get_name() const + { + return std::string( "clock_tree_extractor" ); + } + + std::string ClockTreeExtractorPlugin::get_version() const + { + return std::string( "0.1" ); + } + + std::string ClockTreeExtractorPlugin::get_description() const + { + return "Prototype plugin for extracting and visualizing the clock tree of a digital gate-level netlist."; + } + + void ClockTreeExtractorPlugin::on_load() + { + } + + void ClockTreeExtractorPlugin::on_unload() + { + } + + void ClockTreeExtractorPlugin::initialize() + { + } + + std::set ClockTreeExtractorPlugin::get_dependencies() const + { + std::set retval; + return retval; + } +} // namespace hal \ No newline at end of file From e8ee5c939e1355bdc67917762d369b10ae0e39a9 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 17 Oct 2025 10:32:43 -0400 Subject: [PATCH 02/23] rewrite prototype to prevent incomplete clocktrees --- .../clock_tree_extractor.h | 2 + .../src/clock_tree_extractor.cc | 204 ++++++++++-------- 2 files changed, 111 insertions(+), 95 deletions(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h index e458a1b46999..e7dcc3679e9e 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h @@ -29,6 +29,8 @@ #include "hal_core/defines.h" #include "hal_core/utilities/result.h" +#include +#include #include namespace hal diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc index 8f1d616b18c6..513b7a48f090 100644 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -3,11 +3,26 @@ #include "hal_core/netlist/decorators/netlist_traversal_decorator.h" #include "hal_core/netlist/endpoint.h" #include "hal_core/netlist/gate.h" +#include "hal_core/netlist/gate_library/enums/gate_type_property.h" +#include "hal_core/netlist/gate_library/enums/pin_direction.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/gate_type.h" #include "hal_core/netlist/net.h" +#include "hal_core/netlist/netlist.h" +#include "hal_core/utilities/log.h" #include "hal_core/utilities/result.h" +#include +#include #include #include +#include +#include + +namespace hal +{ + class GatePin; +} namespace hal { @@ -15,26 +30,6 @@ namespace hal { namespace { - bool continue_through_exit_ep( const Endpoint *exit_ep, const u32 current_depth ) - { - if( exit_ep == nullptr ) - { - return false; - } - - const Gate *gate = exit_ep->get_gate(); - const GateType *gate_type = gate->get_type(); - const GateTypeProperty ff = GateTypeProperty::ff; - const GatePin *gate_pin = exit_ep->get_pin(); - - if( gate_type->has_property( ff ) && current_depth == 0 ) - { - return gate_pin->get_direction() == PinDirection::input && gate_pin->get_type() == PinType::clock; - } - - return !( gate_type->has_property( ff ) && current_depth > 0 ); - } - void export_clock_tree( const std::unordered_map> &vertices, const std::vector> edges, const std::string &pathname ) @@ -82,7 +77,7 @@ namespace hal { const std::string edge_color = ( vertices.at( "buffers" ).find( src ) != vertices.at( "buffers" ).end() ) ? "gold" : "green"; - dot_fd << " " << src << " -> " << dst << " [color=" << edge_color << "];"; + dot_fd << " " << src << " -> " << dst << " [color=" << edge_color << "];\n"; } dot_fd << "}\n"; @@ -90,6 +85,14 @@ namespace hal log_info( "clock_tree_extractor", "successfully exported clock tree to '{}'", pathname ); } + + struct GatePairHash + { + std::size_t operator()( const std::pair &pair ) const + { + return std::hash()( pair.first ) ^ ( std::hash()( pair.second ) << 1 ); + } + }; } // namespace ClockTreeExtractor::ClockTreeExtractor( const Netlist *netlist ) @@ -104,115 +107,126 @@ namespace hal return ERR( "no netlist provided" ); } - std::queue queue; - std::unordered_set visited; - std::vector> edges; - std::unordered_map> vertices; + std::queue> queue; NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *m_netlist ); + std::unordered_set, GatePairHash> visited; + std::vector> edges; // TODO: use unordered_set + std::unordered_map> vertices; - const std::vector start_gates = m_netlist->get_gates( + const std::vector ffs = m_netlist->get_gates( []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::ff ); } ); - for( const Gate *gate : start_gates ) + for( const Gate *ff : ffs ) { - queue.push( gate ); - const std::string gate_id = std::to_string( gate->get_id() ); - vertices["ffs"].insert( gate_id ); - } + const u32 ff_id = ff->get_id(); + const std::string ff_name = ff->get_name(); + const std::string ff_id_str = std::to_string( ff_id ); - while( !queue.empty() ) - { - const Gate *current = queue.front(); - const std::string current_id = std::to_string( current->get_id() ); - queue.pop(); + vertices["ffs"].insert( ff_id_str ); - if( auto it = visited.find( current ); it != visited.end() ) + const std::vector clock_pins = ff->get_type()->get_pins( []( const auto &p ) { + return ( p->get_direction() == PinDirection::input ) && ( p->get_type() == PinType::clock ); + } ); + if( clock_pins.size() != 1 ) { - continue; + return ERR( "invalid number of input clock pins at gate '" + ff_name + "' with ID " + ff_id_str ); } - visited.insert( current ); - - auto result = ntd.get_next_matching_gates( - current, - false, - []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::c_buffer ); }, - false, - continue_through_exit_ep, - nullptr ); - if( result.is_error() ) + const Net *clk = ff->get_fan_in_net( clock_pins.front() ); + if( clk == nullptr ) { - return ERR( result.get_error() ); + return ERR( "no net connected to clock pin at gate '" + ff_name + "' with ID " + ff_id_str ); } - const std::set next_matching_gates = result.get(); - if( next_matching_gates.empty() ) + const u32 clk_id = clk->get_id(); + const std::string clk_id_str = std::to_string( clk_id ); + const std::vector clk_sources = clk->get_sources(); + + if( clk_sources.size() > 1 ) { - continue; + return ERR( "invalid number of sources for clock net with ID " + clk_id_str ); } - - for( const Gate *predecessor : next_matching_gates ) + else if( clk->is_global_input_net() ) { - const std::string predecessor_id = std::to_string( predecessor->get_id() ); - std::pair edge = std::make_pair( predecessor_id, current_id ); - if( std::find( edges.begin(), edges.end(), edge ) != edges.end() ) - { - continue; - } - edges.push_back( edge ); - queue.push( predecessor ); - vertices["buffers"].insert( predecessor_id ); + const std::string clk_name = clk->get_name(); + vertices["global_inputs"].insert( clk_name ); + continue; } + + const Endpoint *source_ep = clk_sources.front(); + const Gate *source_gate = source_ep->get_gate(); + + queue.push( { ff, source_gate } ); } - for( const Net *net : m_netlist->get_global_input_nets() ) + while( !queue.empty() ) { - const std::string net_name = net->get_name(); - - auto result = ntd.get_next_matching_gates( - net, - true, - []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::c_buffer ); }, - false, - continue_through_exit_ep, - nullptr ); - if( result.is_error() ) - { - return ERR( result.get_error() ); - } - const std::set next_matching_gates = result.get(); + const std::pair pair = queue.front(); + queue.pop(); + + const Gate *source = pair.second; + Gate *reference = (Gate *) pair.first; - std::size_t childs = 0; - for( const Gate *gate : next_matching_gates ) + if( source->get_type()->has_property( GateTypeProperty::c_buffer ) ) { - if( auto it = visited.find( gate ); it == visited.end() ) + const u32 source_id = source->get_id(); + const u32 reference_id = reference->get_id(); + const std::string source_id_str = std::to_string( source_id ); + const std::string reference_id_str = std::to_string( reference_id ); + const std::pair edge{ source_id_str, reference_id_str }; + + if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) { - continue; + edges.push_back( edge ); } - const u32 gate_id = gate->get_id(); - const std::string gate_id_str = std::to_string( gate_id ); + reference = (Gate *) source; - const std::pair edge = std::make_pair( net_name, gate_id_str ); + const u32 buffer_id = source->get_id(); + const std::string buffer_id_str = std::to_string( buffer_id ); - if( std::find( edges.begin(), edges.end(), edge ) != edges.end() ) + vertices["buffers"].insert( buffer_id_str ); + } + else if( source->get_type()->has_property( GateTypeProperty::ff ) ) + { + // Should the FFS also be traversed? If so, which inputs should be considered? I would assume only + // the data inputs. + continue; + } + + visited.insert( pair ); + + for( const Net *net : source->get_fan_in_nets() ) + { + if( net->is_global_input_net() ) { + const u32 reference_id = reference->get_id(); + const std::string net_name = net->get_name(); + const std::string reference_id_str = std::to_string( reference_id ); + const std::pair edge{ net_name, reference_id_str }; + + if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) + { + edges.push_back( edge ); + } + + vertices["global_inputs"].insert( net_name ); continue; } - edges.push_back( edge ); - childs++; - } - - if( childs > 0 ) - { - vertices["global_inputs"].insert( net_name ); + for( const Endpoint *ep : net->get_sources() ) + { + const Gate *gate = ep->get_gate(); + if( auto it = visited.find( { reference, gate } ); it == visited.end() ) + { + queue.push( { reference, gate } ); + } + } } } - export_clock_tree( vertices, edges, pathname ); - m_edges = edges; + export_clock_tree( vertices, edges, pathname ); return OK( edges.size() ); } From 7cf8a6d60603148c1aaf36c8efefc3c757dfae36 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Wed, 22 Oct 2025 11:09:52 -0400 Subject: [PATCH 03/23] don't fail on unconnected nets, etc. --- .../src/clock_tree_extractor.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc index 513b7a48f090..a7a8062c75a7 100644 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -129,13 +129,16 @@ namespace hal } ); if( clock_pins.size() != 1 ) { - return ERR( "invalid number of input clock pins at gate '" + ff_name + "' with ID " + ff_id_str ); + log_error( "clock_tree_extractor", "invalid number of input clock pins at gate '" + ff_name + "' with ID " + ff_id_str ); + continue; } - const Net *clk = ff->get_fan_in_net( clock_pins.front() ); + const GatePin *clock_pin = clock_pins.front(); + const Net *clk = ff->get_fan_in_net( clock_pin ); if( clk == nullptr ) { - return ERR( "no net connected to clock pin at gate '" + ff_name + "' with ID " + ff_id_str ); + log_error( "clock_tree_extractor", "no net connected to clock pin at gate '" + ff_name + "' with ID " + ff_id_str ); + continue; } const u32 clk_id = clk->get_id(); @@ -144,7 +147,8 @@ namespace hal if( clk_sources.size() > 1 ) { - return ERR( "invalid number of sources for clock net with ID " + clk_id_str ); + log_error( "clock_tree_extractor", "invalid number of sources for clock net with ID " + clk_id_str ); + continue; } else if( clk->is_global_input_net() ) { @@ -152,6 +156,11 @@ namespace hal vertices["global_inputs"].insert( clk_name ); continue; } + else if( clk_sources.size() == 0 ) + { + // ignore unrouted nets for now + continue; + } const Endpoint *source_ep = clk_sources.front(); const Gate *source_gate = source_ep->get_gate(); From 1f5e7db404c8efbe2e22f56e4b4978c1c6a3e3e6 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Wed, 22 Oct 2025 11:37:19 -0400 Subject: [PATCH 04/23] don't traverse control signals of clock gates --- .../src/clock_tree_extractor.cc | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc index a7a8062c75a7..c86cb408c75d 100644 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -93,6 +93,12 @@ namespace hal return std::hash()( pair.first ) ^ ( std::hash()( pair.second ) << 1 ); } }; + + bool is_control_pin( const PinType &pin_type ) + { + return pin_type == PinType::clock || pin_type == PinType::enable || pin_type == PinType::select + || pin_type == PinType::set || pin_type == PinType::reset; + } } // namespace ClockTreeExtractor::ClockTreeExtractor( const Netlist *netlist ) @@ -129,15 +135,17 @@ namespace hal } ); if( clock_pins.size() != 1 ) { - log_error( "clock_tree_extractor", "invalid number of input clock pins at gate '" + ff_name + "' with ID " + ff_id_str ); + log_error( "clock_tree_extractor", + "invalid number of input clock pins at gate '" + ff_name + "' with ID " + ff_id_str ); continue; } - const GatePin *clock_pin = clock_pins.front(); + const GatePin *clock_pin = clock_pins.front(); const Net *clk = ff->get_fan_in_net( clock_pin ); if( clk == nullptr ) { - log_error( "clock_tree_extractor", "no net connected to clock pin at gate '" + ff_name + "' with ID " + ff_id_str ); + log_error( "clock_tree_extractor", + "no net connected to clock pin at gate '" + ff_name + "' with ID " + ff_id_str ); continue; } @@ -147,7 +155,8 @@ namespace hal if( clk_sources.size() > 1 ) { - log_error( "clock_tree_extractor", "invalid number of sources for clock net with ID " + clk_id_str ); + log_error( "clock_tree_extractor", + "invalid number of sources for clock net with ID " + clk_id_str ); continue; } else if( clk->is_global_input_net() ) @@ -173,7 +182,7 @@ namespace hal const std::pair pair = queue.front(); queue.pop(); - const Gate *source = pair.second; + Gate *source = (Gate *) pair.second; Gate *reference = (Gate *) pair.first; if( source->get_type()->has_property( GateTypeProperty::c_buffer ) ) @@ -205,8 +214,16 @@ namespace hal visited.insert( pair ); - for( const Net *net : source->get_fan_in_nets() ) + for( const Endpoint *ep : source->get_fan_in_endpoints() ) { + const PinType pin_type = ep->get_pin()->get_type(); + if( is_control_pin( pin_type ) ) + { + // Don't traverse control signals of clock gates + continue; + } + + const Net *net = ep->get_net(); if( net->is_global_input_net() ) { const u32 reference_id = reference->get_id(); From 333499edfa552da1e2e54986d8032665bfab4ee1 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Wed, 22 Oct 2025 11:42:58 -0400 Subject: [PATCH 05/23] don't traverse latches for now --- plugins/clock_tree_extractor/src/clock_tree_extractor.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc index c86cb408c75d..370efc5b3a7b 100644 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -205,7 +205,8 @@ namespace hal vertices["buffers"].insert( buffer_id_str ); } - else if( source->get_type()->has_property( GateTypeProperty::ff ) ) + else if( source->get_type()->has_property( GateTypeProperty::ff ) + || source->get_type()->has_property( GateTypeProperty::latch ) ) { // Should the FFS also be traversed? If so, which inputs should be considered? I would assume only // the data inputs. From 0b2bfd216a9f4042adb302b27c549e4d907ac37d Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 24 Oct 2025 16:05:44 -0400 Subject: [PATCH 06/23] some small improvements for low quality netlists --- .../src/clock_tree_extractor.cc | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc index 370efc5b3a7b..de616b13205b 100644 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -217,7 +217,9 @@ namespace hal for( const Endpoint *ep : source->get_fan_in_endpoints() ) { - const PinType pin_type = ep->get_pin()->get_type(); + const GatePin *pin = ep->get_pin(); + const PinType pin_type = pin->get_type(); + if( is_control_pin( pin_type ) ) { // Don't traverse control signals of clock gates @@ -225,6 +227,14 @@ namespace hal } const Net *net = ep->get_net(); + const u32 net_id = net->get_id(); + const std::string net_name = net->get_name(); + + if(net_name == "'0'" || net_name == "'1'") + { + continue; + } + if( net->is_global_input_net() ) { const u32 reference_id = reference->get_id(); @@ -241,18 +251,30 @@ namespace hal continue; } - for( const Endpoint *ep : net->get_sources() ) + const u32 num_net_sources = net->get_num_of_sources(); + + if(num_net_sources == 0) { - const Gate *gate = ep->get_gate(); - if( auto it = visited.find( { reference, gate } ); it == visited.end() ) - { - queue.push( { reference, gate } ); - } + log_warning("clock_tree_extractor", "unrouted clock net with ID {} ignored", net_id); + continue; + } + else if(num_net_sources > 1) + { + log_warning("clock_tree_extractor", "multi-driven clock net with ID {} ignored", net_id); + continue; + } + + const std::vector net_sources = net->get_sources(); + const Endpoint *source_ep = net_sources.front(); + const Gate *gate = source_ep->get_gate(); + + if( auto it = visited.find( { reference, gate } ); it == visited.end() ) + { + queue.push( { reference, gate } ); } } } - m_edges = edges; export_clock_tree( vertices, edges, pathname ); return OK( edges.size() ); From fff09d294a175edf043091ac44855c8ba53f64c1 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Tue, 28 Oct 2025 14:20:21 -0400 Subject: [PATCH 07/23] include toggle flip-flop-based clock dividers --- .../src/clock_tree_extractor.cc | 124 +++++++++++++----- 1 file changed, 94 insertions(+), 30 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc index de616b13205b..f131e7f0b5d4 100644 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc +++ b/plugins/clock_tree_extractor/src/clock_tree_extractor.cc @@ -75,8 +75,17 @@ namespace hal for( const auto &[src, dst] : edges ) { - const std::string edge_color = - ( vertices.at( "buffers" ).find( src ) != vertices.at( "buffers" ).end() ) ? "gold" : "green"; + std::string edge_color = "green"; + + if( vertices.at( "buffers" ).find( src ) != vertices.at( "buffers" ).end() ) + { + edge_color = "gold"; + } + else if( vertices.at( "ffs" ).find( src ) != vertices.at( "ffs" ).end() ) + { + edge_color = "dodgerblue"; + } + dot_fd << " " << src << " -> " << dst << " [color=" << edge_color << "];\n"; } @@ -99,6 +108,70 @@ namespace hal return pin_type == PinType::clock || pin_type == PinType::enable || pin_type == PinType::select || pin_type == PinType::set || pin_type == PinType::reset; } + + void insert_edge( std::vector> &edges, + const Gate *source, + const Gate *reference ) + { + const u32 source_id = source->get_id(); + const u32 reference_id = reference->get_id(); + const std::string source_id_str = std::to_string( source_id ); + const std::string reference_id_str = std::to_string( reference_id ); + const std::pair edge{ source_id_str, reference_id_str }; + + if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) + { + edges.push_back( edge ); + } + } + + void insert_edge( std::vector> &edges, + const Net *source, + const Gate *reference ) + { + const u32 reference_id = reference->get_id(); + const std::string source_net_name = source->get_name(); + const std::string reference_id_str = std::to_string( reference_id ); + const std::pair edge{ source_net_name, reference_id_str }; + + if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) + { + edges.push_back( edge ); + } + } + + const std::vector get_toggle_ffs( const Netlist *netlist ) + { + const std::vector ffs = netlist->get_gates( + []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::ff ); } ); + + std::vector result; + for( const Gate *ff : ffs ) + { + const std::vector successor_endpoints = ff->get_successors(); + const std::size_t successor_endpoints_size = successor_endpoints.size(); + + if( successor_endpoints_size == 0 ) + { + continue; + } + + std::vector successors; + successors.reserve( successor_endpoints_size ); + + std::transform( successor_endpoints.begin(), + successor_endpoints.end(), + std::back_inserter( successors ), + []( const Endpoint *ep ) { return ep->get_gate(); } ); + + if( std::find( successors.begin(), successors.end(), ff ) != successors.end() ) + { + result.push_back( ff ); + } + } + + return result; + } } // namespace ClockTreeExtractor::ClockTreeExtractor( const Netlist *netlist ) @@ -177,6 +250,8 @@ namespace hal queue.push( { ff, source_gate } ); } + const std::vector toggle_ffs = get_toggle_ffs( m_netlist ); + while( !queue.empty() ) { const std::pair pair = queue.front(); @@ -187,16 +262,7 @@ namespace hal if( source->get_type()->has_property( GateTypeProperty::c_buffer ) ) { - const u32 source_id = source->get_id(); - const u32 reference_id = reference->get_id(); - const std::string source_id_str = std::to_string( source_id ); - const std::string reference_id_str = std::to_string( reference_id ); - const std::pair edge{ source_id_str, reference_id_str }; - - if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) - { - edges.push_back( edge ); - } + insert_edge( edges, source, reference ); reference = (Gate *) source; @@ -205,11 +271,17 @@ namespace hal vertices["buffers"].insert( buffer_id_str ); } - else if( source->get_type()->has_property( GateTypeProperty::ff ) - || source->get_type()->has_property( GateTypeProperty::latch ) ) + else if( source->get_type()->has_property( GateTypeProperty::ff ) ) + { + if( std::find( toggle_ffs.begin(), toggle_ffs.end(), source ) != toggle_ffs.end() ) + { + insert_edge( edges, source, reference ); + } + + continue; + } + else if( source->get_type()->has_property( GateTypeProperty::latch ) ) { - // Should the FFS also be traversed? If so, which inputs should be considered? I would assume only - // the data inputs. continue; } @@ -230,22 +302,14 @@ namespace hal const u32 net_id = net->get_id(); const std::string net_name = net->get_name(); - if(net_name == "'0'" || net_name == "'1'") + if( net_name == "'0'" || net_name == "'1'" ) { continue; } if( net->is_global_input_net() ) { - const u32 reference_id = reference->get_id(); - const std::string net_name = net->get_name(); - const std::string reference_id_str = std::to_string( reference_id ); - const std::pair edge{ net_name, reference_id_str }; - - if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) - { - edges.push_back( edge ); - } + insert_edge( edges, net, reference ); vertices["global_inputs"].insert( net_name ); continue; @@ -253,14 +317,14 @@ namespace hal const u32 num_net_sources = net->get_num_of_sources(); - if(num_net_sources == 0) + if( num_net_sources == 0 ) { - log_warning("clock_tree_extractor", "unrouted clock net with ID {} ignored", net_id); + log_warning( "clock_tree_extractor", "unrouted clock net with ID {} ignored", net_id ); continue; } - else if(num_net_sources > 1) + else if( num_net_sources > 1 ) { - log_warning("clock_tree_extractor", "multi-driven clock net with ID {} ignored", net_id); + log_warning( "clock_tree_extractor", "multi-driven clock net with ID {} ignored", net_id ); continue; } From 5d3a15ded3be59c4c5d7d83abc215524847948a9 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Mon, 10 Nov 2025 09:20:35 -0500 Subject: [PATCH 08/23] change source file suffix --- .../python/{python_bindings.cc => python_bindings.cpp} | 0 .../src/{clock_tree_extractor.cc => clock_tree_extractor.cpp} | 0 ...in_clock_tree_extractor.cc => plugin_clock_tree_extractor.cpp} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename plugins/clock_tree_extractor/python/{python_bindings.cc => python_bindings.cpp} (100%) rename plugins/clock_tree_extractor/src/{clock_tree_extractor.cc => clock_tree_extractor.cpp} (100%) rename plugins/clock_tree_extractor/src/{plugin_clock_tree_extractor.cc => plugin_clock_tree_extractor.cpp} (100%) diff --git a/plugins/clock_tree_extractor/python/python_bindings.cc b/plugins/clock_tree_extractor/python/python_bindings.cpp similarity index 100% rename from plugins/clock_tree_extractor/python/python_bindings.cc rename to plugins/clock_tree_extractor/python/python_bindings.cpp diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/clock_tree_extractor.cpp similarity index 100% rename from plugins/clock_tree_extractor/src/clock_tree_extractor.cc rename to plugins/clock_tree_extractor/src/clock_tree_extractor.cpp diff --git a/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cc b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp similarity index 100% rename from plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cc rename to plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp From 62e9afaf7c79d34caba476570e4fcf01cd3a8e46 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Wed, 12 Nov 2025 16:42:45 -0500 Subject: [PATCH 09/23] rewrite to use igraph internally --- plugins/clock_tree_extractor/CMakeLists.txt | 4 +- .../{clock_tree_extractor.h => clock_tree.h} | 49 +- .../python/python_bindings.cpp | 60 +-- .../clock_tree_extractor/src/clock_tree.cpp | 460 ++++++++++++++++++ .../src/clock_tree_extractor.cpp | 352 -------------- 5 files changed, 529 insertions(+), 396 deletions(-) rename plugins/clock_tree_extractor/include/clock_tree_extractor/{clock_tree_extractor.h => clock_tree.h} (61%) create mode 100644 plugins/clock_tree_extractor/src/clock_tree.cpp delete mode 100644 plugins/clock_tree_extractor/src/clock_tree_extractor.cpp diff --git a/plugins/clock_tree_extractor/CMakeLists.txt b/plugins/clock_tree_extractor/CMakeLists.txt index b234ac639a5d..1fccdf0e035e 100644 --- a/plugins/clock_tree_extractor/CMakeLists.txt +++ b/plugins/clock_tree_extractor/CMakeLists.txt @@ -10,8 +10,8 @@ if(PL_CLOCK_TREE_EXTRACTOR OR BUILD_ALL_PLUGINS) endif() file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) - file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc) - file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cc) + file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + file(GLOB_RECURSE CLOCK_TREE_EXTRACTOR_PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cpp) hal_add_plugin(clock_tree_extractor SHARED diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h similarity index 61% rename from plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h rename to plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index e7dcc3679e9e..e9872054e90d 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree_extractor.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -29,8 +29,12 @@ #include "hal_core/defines.h" #include "hal_core/utilities/result.h" +#include +#include #include -#include +#include +#include +#include #include namespace hal @@ -45,35 +49,52 @@ namespace hal namespace hal { - class GateType; -} - -namespace hal -{ - class Endpoint; + class Net; } namespace hal { namespace cte { - class ClockTreeExtractor + class ClockTree { public: - ClockTreeExtractor() = default; + ~ClockTree(); + + static Result> from_netlist( const Netlist *netlist ); + + Result export_dot( const std::string &pathname ) const; + + const Gate *get_gate_from_vertex( igraph_integer_t vertex ) const; + + const igraph_integer_t get_vertex_from_gate( const Gate *gate ) const; - ClockTreeExtractor( const Netlist *netlist ); + const Net *get_net_from_vertex( igraph_integer_t vertex ) const; - ~ClockTreeExtractor() = default; + const igraph_integer_t get_vertex_from_net( const Net *net ) const; - Result analyze( const std::string &pathname ); + const Netlist *get_netlist() const; - const std::vector> get_edges() const; + const igraph_t *get_igraph() const; private: + ClockTree() = delete; + + ClockTree( const Netlist *netlist ); + const Netlist *m_netlist; - std::vector> m_edges; + igraph_t m_igraph; + + igraph_t *m_igraph_ptr; + + std::unordered_set m_roots; + + std::unordered_map m_vertices_to_ptrs; + + std::unordered_map m_ptrs_to_vertices; + + std::unordered_map m_ptrs_to_types; }; } // namespace cte } // namespace hal diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index 9b3a40fda8cc..e64338093c76 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -1,9 +1,10 @@ #include "hal_core/python_bindings/python_bindings.h" -#include "clock_tree_extractor/clock_tree_extractor.h" +#include "clock_tree_extractor/clock_tree.h" #include "clock_tree_extractor/plugin_clock_tree_extractor.h" #include "pybind11/pybind11.h" +#include #include #include #include @@ -95,33 +96,36 @@ namespace hal :rtype: set[str] )" ); - py::class_> py_clock_tree_extractor( - m, "ClockTreeExtractor", R"( - - )" ); - - py_clock_tree_extractor.def( py::init(), py::arg( "netlist" ), R"( - - )" ); - - py_clock_tree_extractor.def( - "analyze", - []( cte::ClockTreeExtractor &self, const std::string &pathname ) -> std::optional { - auto res = self.analyze( pathname ); - if( res.is_ok() ) - { - return res.get(); - } - else - { - log_error( "python_context", "{}", res.get_error().get() ); - return std::nullopt; - } - }, - py::arg( "pathname" ), - R"( - - )" ); + py::class_>( m, "ClockTree", R"()" ) + .def_static( + "from_netlist", + []( const Netlist *netlist ) -> std::unique_ptr { + auto result = cte::ClockTree::from_netlist( netlist ); + if( result.is_ok() ) + { + return result.get(); + } + + log_error( "clock_tree_extractor", "{}", result.get_error().get() ); + return nullptr; + }, + py::arg( "netlist" ), + R"()" ) + .def( + "export", + []( const cte::ClockTree &self, const std::string &pathname ) -> bool { + auto result = self.export_dot( pathname ); + if( result.is_ok() ) + { + return true; + } + + log_error( "clock_tree_extractor", "{}", result.get_error().get() ); + return false; + }, + py::arg( "pathname" ), + R"()" ) + .def( "get_netlist", &cte::ClockTree::get_netlist, R"()" ); #ifndef PYBIND11_MODULE return m.ptr(); diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp new file mode 100644 index 000000000000..f31510a54262 --- /dev/null +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -0,0 +1,460 @@ +#include "clock_tree_extractor/clock_tree.h" + +#include "hal_core/netlist/decorators/netlist_traversal_decorator.h" +#include "hal_core/netlist/endpoint.h" +#include "hal_core/netlist/gate.h" +#include "hal_core/netlist/gate_library/enums/gate_type_property.h" +#include "hal_core/netlist/gate_library/enums/pin_direction.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/gate_type.h" +#include "hal_core/netlist/net.h" +#include "hal_core/netlist/netlist.h" +#include "hal_core/utilities/log.h" +#include "hal_core/utilities/result.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace hal +{ + namespace cte + { + namespace + { + struct VoidPtrHash + { + std::size_t operator()( const std::pair &pair ) const + { + return std::hash()( pair.first ) ^ ( std::hash()( pair.second ) << 1 ); + } + }; + + struct PairPtrEq + { + bool operator()( const std::pair &p1, + const std::pair &p2 ) const noexcept + { + return p1.first == p2.first && p1.second == p2.second; + } + }; + + inline bool is_ff( const Gate *gate ) + { + return gate->get_type()->has_property( GateTypeProperty::ff ); + } + + inline bool is_latch( const Gate *gate ) + { + return gate->get_type()->has_property( GateTypeProperty::latch ); + } + + inline bool is_buffer( const Gate *gate ) + { + return gate->get_type()->has_property( GateTypeProperty::c_buffer ); + } + + inline bool is_inverter( const Gate *gate ) + { + return gate->get_type()->has_property( GateTypeProperty::c_inverter ); + } + + inline bool is_control_pin( const PinType &pin_type ) + { + return pin_type == PinType::clock || pin_type == PinType::enable || pin_type == PinType::select + || pin_type == PinType::set || pin_type == PinType::reset; + } + + inline bool is_connected_to_control_pin( const Endpoint *endpoint ) + { + return is_control_pin( endpoint->get_pin()->get_type() ); + } + + const std::unordered_set get_toggle_ffs( const Netlist *netlist ) + { + const std::vector ffs = netlist->get_gates( is_ff ); + + std::unordered_set result; + for( const Gate *ff : ffs ) + { + const std::vector successor_endpoints = ff->get_successors(); + const std::size_t successor_endpoints_size = successor_endpoints.size(); + + if( successor_endpoints_size == 0 ) + { + continue; + } + + std::vector successors; + successors.reserve( successor_endpoints_size ); + + std::transform( successor_endpoints.begin(), + successor_endpoints.end(), + std::back_inserter( successors ), + []( const Endpoint *ep ) { return ep->get_gate(); } ); + + if( std::find( successors.begin(), successors.end(), ff ) != successors.end() ) + { + result.insert( ff ); + } + } + + return result; + } + } // namespace + + ClockTree::ClockTree( const Netlist *netlist ) + : m_netlist( netlist ) + , m_igraph_ptr( &m_igraph ) + { + } + + ClockTree::~ClockTree() + { + igraph_destroy( &m_igraph ); + } + + Result> ClockTree::from_netlist( const Netlist *netlist ) + { + if( netlist == nullptr ) + { + return ERR( "no netlist provided" ); + } + + std::unordered_set vertices; + std::unordered_set, VoidPtrHash> edges; + std::unordered_map ptrs_to_type; + + std::queue> queue; + NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *netlist ); + std::unordered_set, VoidPtrHash> visited; + + for( const Gate *ff : netlist->get_gates( is_ff ) ) + { + const std::vector clock_pins = ff->get_type()->get_pins( []( const auto &p ) { + return ( p->get_direction() == PinDirection::input ) && ( p->get_type() == PinType::clock ); + } ); + + if( clock_pins.size() != 1 ) + { + log_error( "clock_tree_extractor", + "invalid number of input clock pins at gate '" + ff->get_name() + "' with ID " + + std::to_string( ff->get_id() ) ); + continue; + } + + const Net *clk = ff->get_fan_in_net( clock_pins.front() ); + if( clk == nullptr ) + { + log_error( "clock_tree_extractor", + "no net connected to clock pin at gate '" + ff->get_name() + "' with ID " + + std::to_string( ff->get_id() ) ); + continue; + } + + if( clk->get_num_of_sources() > 1 ) + { + log_error( "clock_tree_extractor", + "invalid number of sources for clock net with ID " + std::to_string( clk->get_id() ) ); + continue; + } + else if( clk->is_global_input_net() ) + { + vertices.insert( (void *) clk ); + ptrs_to_type[(void *) clk] = "net"; + continue; + } + else if( clk->get_num_of_sources() == 0 ) + { + continue; + } + + queue.push( { ff, clk->get_sources().front()->get_gate() } ); + + vertices.insert( (void *) ff ); + ptrs_to_type[(void *) ff] = "gate"; + } + + const std::unordered_set toggle_ffs = get_toggle_ffs( netlist ); + + while( !queue.empty() ) + { + const std::pair pair = queue.front(); + queue.pop(); + + Gate *source = (Gate *) pair.second; + Gate *reference = (Gate *) pair.first; + + if( is_latch( source ) ) + { + // Ignore latches + continue; + } + else if( is_buffer( source ) || is_inverter( source ) || is_ff( source ) ) + { + if( is_ff( source ) && toggle_ffs.find( source ) == toggle_ffs.end() ) + { + // Include only toggle flip-flops for now + continue; + } + + vertices.insert( (void *) source ); + vertices.insert( (void *) reference ); + + ptrs_to_type[(void *) source] = "gate"; + ptrs_to_type[(void *) reference] = "gate"; + + edges.insert( { (void *) source, (void *) reference } ); + + if( is_ff( source ) ) + { + continue; + } + + reference = (Gate *) source; + } + + visited.insert( pair ); + + for( const Endpoint *ep : source->get_fan_in_endpoints() ) + { + if( is_connected_to_control_pin( ep ) ) + { + // Don't traverse control signals of clock gates + continue; + } + + const Net *net = ep->get_net(); + if( net->get_name() == "'0'" || net->get_name() == "'1'" ) + { + // Don't traverse power/ground signals + continue; + } + + if( net->is_global_input_net() ) + { + vertices.insert( (void *) net ); + vertices.insert( (void *) reference ); + + ptrs_to_type[(void *) net] = "net"; + ptrs_to_type[(void *) reference] = "gate"; + + edges.insert( { (void *) net, (void *) reference } ); + continue; + } + + if( net->get_num_of_sources() == 0 ) + { + log_warning( "clock_tree_extractor", + "unrouted clock net with ID {} ignored", + std::to_string( net->get_id() ) ); + continue; + } + else if( net->get_num_of_sources() > 1 ) + { + log_warning( "clock_tree_extractor", + "multi-driven clock net with ID {} ignored", + std::to_string( net->get_id() ) ); + continue; + } + + const Gate *new_source = net->get_sources().front()->get_gate(); + if( visited.find( { reference, new_source } ) == visited.end() ) + { + queue.push( { reference, new_source } ); + } + } + } + + std::unique_ptr clock_tree = std::unique_ptr( new ClockTree( netlist ) ); + + igraph_integer_t idx = 0; + for( const void *vertex : vertices ) + { + const igraph_integer_t vertex_id = idx++; + + clock_tree->m_vertices_to_ptrs[vertex_id] = vertex; + clock_tree->m_ptrs_to_vertices[vertex] = vertex_id; + } + + clock_tree->m_ptrs_to_types = ptrs_to_type; + + igraph_error_t ierror; + igraph_vector_int_t iedges; + if( ( ierror = igraph_vector_int_init( &iedges, 2 * edges.size() ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + idx = 0; + for( const auto &[src, dst] : edges ) + { + VECTOR( iedges )[idx++] = clock_tree->m_ptrs_to_vertices.at( src ); + VECTOR( iedges )[idx++] = clock_tree->m_ptrs_to_vertices.at( dst ); + } + + if( ( ierror = igraph_create( clock_tree->m_igraph_ptr, &iedges, vertices.size(), IGRAPH_DIRECTED ) ) + != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &iedges ); + return ERR( igraph_strerror( ierror ) ); + } + + igraph_vector_int_destroy( &iedges ); + + igraph_vector_int_t indegrees; + if( ( ierror = igraph_vector_int_init( &indegrees, 0 ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + if( ( ierror = igraph_degree( + clock_tree->m_igraph_ptr, &indegrees, igraph_vss_all(), IGRAPH_IN, IGRAPH_NO_LOOPS ) ) + != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &indegrees ); + return ERR( igraph_strerror( ierror ) ); + } + + for( idx = 0; idx < igraph_vector_int_size( &indegrees ); idx++ ) + { + if( VECTOR( indegrees )[idx] != 0 ) + { + continue; + } + clock_tree->m_roots.insert( idx ); + } + + igraph_vector_int_destroy( &indegrees ); + + return OK( std::move( clock_tree ) ); + } + + Result ClockTree::export_dot( const std::string &pathname ) const + { + std::ofstream dot_fd( pathname ); + + if( !dot_fd ) + { + return ERR( "couldn't export clock tree to '" + pathname + "'" ); + } + + dot_fd << "digraph { comment=\"created by HAL plugin clock_tree_extractor\"\n"; + + for( const auto &[ptr, vertex] : m_ptrs_to_vertices ) + { + if( m_ptrs_to_types.at( ptr ) == "net" ) + { + dot_fd << " " << ( (Net *) ptr )->get_name() << " [shape=circle];\n"; + continue; + } + + const i32 x = ( (Gate *) ptr )->get_location_x(); + const i32 y = ( (Gate *) ptr )->get_location_y(); + + const std::string coords = + ( x < 0 || y < 0 ) ? "" : "x=" + std::to_string( x ) + " y=" + std::to_string( y ); + + if( is_buffer( (Gate *) ptr ) ) + { + dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << " [" << coords + << " shape=rectangle];\n"; + } + else if( is_inverter( (Gate *) ptr ) ) + { + dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << " [" << coords + << " shape=triangle, orientation=180];\n"; + } + else if( is_ff( (Gate *) ptr ) ) + { + dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) + << ( coords.size() == 0 ? ";\n" : " [" + coords + "];\n" ); + } + } + + std::queue> queue; + for( const igraph_integer_t &root : m_roots ) + { + queue.push( { root, "blue" } ); + } + + igraph_error_t ierror; + std::unordered_set visited; + while( !queue.empty() ) + { + const std::pair pair = queue.front(); + queue.pop(); + + const igraph_integer_t vertex = pair.first; + std::string edge_color = pair.second; + + if( visited.find( vertex ) != visited.end() ) + { + continue; + } + + visited.insert( vertex ); + + const void *sptr = m_vertices_to_ptrs.at( vertex ); + const std::string stype = m_ptrs_to_types.at( sptr ); + + if( stype == "gate" && is_inverter( (Gate *) sptr ) ) + { + edge_color = edge_color == "red" ? "blue" : "red"; + } + + igraph_vector_int_t neighbors; + if( ( ierror = igraph_vector_int_init( &neighbors, 0 ) ) != IGRAPH_SUCCESS ) + { + dot_fd.close(); + return ERR( igraph_strerror( ierror ) ); + } + + if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, vertex, IGRAPH_OUT ) ) != IGRAPH_SUCCESS ) + { + dot_fd.close(); + igraph_vector_int_destroy( &neighbors ); + return ERR( igraph_strerror( ierror ) ); + } + + for( igraph_integer_t idx = 0; idx < igraph_vector_int_size( &neighbors ); idx++ ) + { + const std::string src_id = + stype == "gate" ? std::to_string( ( (Gate *) sptr )->get_id() ) : ( (Net *) sptr )->get_name(); + + const void *dptr = m_vertices_to_ptrs.at( VECTOR( neighbors )[idx] ); + const std::string dtype = m_ptrs_to_types.at( dptr ); + const std::string dst_id = + dtype == "gate" ? std::to_string( ( (Gate *) dptr )->get_id() ) : ( (Net *) dptr )->get_name(); + + dot_fd << " " << src_id << " -> " << dst_id << " [color=" << edge_color << "];\n"; + queue.push( { VECTOR( neighbors )[idx], edge_color } ); + } + + igraph_vector_int_destroy( &neighbors ); + } + + dot_fd << "}\n"; + dot_fd.close(); + + return OK( {} ); + } + + const Netlist *ClockTree::get_netlist() const + { + return m_netlist; + } + + const igraph_t *ClockTree::get_igraph() const + { + return m_igraph_ptr; + } + } // namespace cte +} // namespace hal + +// BUG: looks like ~20 vertices plus their edges are missing in benchmark +// TODO: investigate possible BUG diff --git a/plugins/clock_tree_extractor/src/clock_tree_extractor.cpp b/plugins/clock_tree_extractor/src/clock_tree_extractor.cpp deleted file mode 100644 index f131e7f0b5d4..000000000000 --- a/plugins/clock_tree_extractor/src/clock_tree_extractor.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#include "clock_tree_extractor/clock_tree_extractor.h" - -#include "hal_core/netlist/decorators/netlist_traversal_decorator.h" -#include "hal_core/netlist/endpoint.h" -#include "hal_core/netlist/gate.h" -#include "hal_core/netlist/gate_library/enums/gate_type_property.h" -#include "hal_core/netlist/gate_library/enums/pin_direction.h" -#include "hal_core/netlist/gate_library/enums/pin_type.h" -#include "hal_core/netlist/gate_library/gate_type.h" -#include "hal_core/netlist/net.h" -#include "hal_core/netlist/netlist.h" -#include "hal_core/utilities/log.h" -#include "hal_core/utilities/result.h" - -#include -#include -#include -#include -#include -#include - -namespace hal -{ - class GatePin; -} - -namespace hal -{ - namespace cte - { - namespace - { - void export_clock_tree( const std::unordered_map> &vertices, - const std::vector> edges, - const std::string &pathname ) - { - if( pathname == "" ) - { - return; - } - - std::ofstream dot_fd( pathname ); - - if( !dot_fd ) - { - log_error( "clock_tree_extractor", "could not export clock tree to '{}'", pathname ); - return; - } - - dot_fd << "digraph {\n"; - - if( vertices.find( "global_inputs" ) != vertices.end() ) - { - for( const std::string &net_name : vertices.at( "global_inputs" ) ) - { - dot_fd << " " << net_name << " [color=green];\n"; - } - } - - if( vertices.find( "ffs" ) != vertices.end() ) - { - for( const std::string &gate_id : vertices.at( "ffs" ) ) - { - dot_fd << " " << gate_id << " [color=dodgerblue];\n"; - } - } - - if( vertices.find( "buffers" ) != vertices.end() ) - { - for( const std::string &gate_id : vertices.at( "buffers" ) ) - { - dot_fd << " " << gate_id << " [color=gold];\n"; - } - } - - for( const auto &[src, dst] : edges ) - { - std::string edge_color = "green"; - - if( vertices.at( "buffers" ).find( src ) != vertices.at( "buffers" ).end() ) - { - edge_color = "gold"; - } - else if( vertices.at( "ffs" ).find( src ) != vertices.at( "ffs" ).end() ) - { - edge_color = "dodgerblue"; - } - - dot_fd << " " << src << " -> " << dst << " [color=" << edge_color << "];\n"; - } - - dot_fd << "}\n"; - dot_fd.close(); - - log_info( "clock_tree_extractor", "successfully exported clock tree to '{}'", pathname ); - } - - struct GatePairHash - { - std::size_t operator()( const std::pair &pair ) const - { - return std::hash()( pair.first ) ^ ( std::hash()( pair.second ) << 1 ); - } - }; - - bool is_control_pin( const PinType &pin_type ) - { - return pin_type == PinType::clock || pin_type == PinType::enable || pin_type == PinType::select - || pin_type == PinType::set || pin_type == PinType::reset; - } - - void insert_edge( std::vector> &edges, - const Gate *source, - const Gate *reference ) - { - const u32 source_id = source->get_id(); - const u32 reference_id = reference->get_id(); - const std::string source_id_str = std::to_string( source_id ); - const std::string reference_id_str = std::to_string( reference_id ); - const std::pair edge{ source_id_str, reference_id_str }; - - if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) - { - edges.push_back( edge ); - } - } - - void insert_edge( std::vector> &edges, - const Net *source, - const Gate *reference ) - { - const u32 reference_id = reference->get_id(); - const std::string source_net_name = source->get_name(); - const std::string reference_id_str = std::to_string( reference_id ); - const std::pair edge{ source_net_name, reference_id_str }; - - if( auto it = std::find( edges.begin(), edges.end(), edge ); it == edges.end() ) - { - edges.push_back( edge ); - } - } - - const std::vector get_toggle_ffs( const Netlist *netlist ) - { - const std::vector ffs = netlist->get_gates( - []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::ff ); } ); - - std::vector result; - for( const Gate *ff : ffs ) - { - const std::vector successor_endpoints = ff->get_successors(); - const std::size_t successor_endpoints_size = successor_endpoints.size(); - - if( successor_endpoints_size == 0 ) - { - continue; - } - - std::vector successors; - successors.reserve( successor_endpoints_size ); - - std::transform( successor_endpoints.begin(), - successor_endpoints.end(), - std::back_inserter( successors ), - []( const Endpoint *ep ) { return ep->get_gate(); } ); - - if( std::find( successors.begin(), successors.end(), ff ) != successors.end() ) - { - result.push_back( ff ); - } - } - - return result; - } - } // namespace - - ClockTreeExtractor::ClockTreeExtractor( const Netlist *netlist ) - { - m_netlist = netlist; - } - - Result ClockTreeExtractor::analyze( const std::string &pathname = "" ) - { - if( m_netlist == nullptr ) - { - return ERR( "no netlist provided" ); - } - - std::queue> queue; - NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *m_netlist ); - std::unordered_set, GatePairHash> visited; - std::vector> edges; // TODO: use unordered_set - std::unordered_map> vertices; - - const std::vector ffs = m_netlist->get_gates( - []( const Gate *g ) { return g->get_type()->has_property( GateTypeProperty::ff ); } ); - - for( const Gate *ff : ffs ) - { - const u32 ff_id = ff->get_id(); - const std::string ff_name = ff->get_name(); - const std::string ff_id_str = std::to_string( ff_id ); - - vertices["ffs"].insert( ff_id_str ); - - const std::vector clock_pins = ff->get_type()->get_pins( []( const auto &p ) { - return ( p->get_direction() == PinDirection::input ) && ( p->get_type() == PinType::clock ); - } ); - if( clock_pins.size() != 1 ) - { - log_error( "clock_tree_extractor", - "invalid number of input clock pins at gate '" + ff_name + "' with ID " + ff_id_str ); - continue; - } - - const GatePin *clock_pin = clock_pins.front(); - const Net *clk = ff->get_fan_in_net( clock_pin ); - if( clk == nullptr ) - { - log_error( "clock_tree_extractor", - "no net connected to clock pin at gate '" + ff_name + "' with ID " + ff_id_str ); - continue; - } - - const u32 clk_id = clk->get_id(); - const std::string clk_id_str = std::to_string( clk_id ); - const std::vector clk_sources = clk->get_sources(); - - if( clk_sources.size() > 1 ) - { - log_error( "clock_tree_extractor", - "invalid number of sources for clock net with ID " + clk_id_str ); - continue; - } - else if( clk->is_global_input_net() ) - { - const std::string clk_name = clk->get_name(); - vertices["global_inputs"].insert( clk_name ); - continue; - } - else if( clk_sources.size() == 0 ) - { - // ignore unrouted nets for now - continue; - } - - const Endpoint *source_ep = clk_sources.front(); - const Gate *source_gate = source_ep->get_gate(); - - queue.push( { ff, source_gate } ); - } - - const std::vector toggle_ffs = get_toggle_ffs( m_netlist ); - - while( !queue.empty() ) - { - const std::pair pair = queue.front(); - queue.pop(); - - Gate *source = (Gate *) pair.second; - Gate *reference = (Gate *) pair.first; - - if( source->get_type()->has_property( GateTypeProperty::c_buffer ) ) - { - insert_edge( edges, source, reference ); - - reference = (Gate *) source; - - const u32 buffer_id = source->get_id(); - const std::string buffer_id_str = std::to_string( buffer_id ); - - vertices["buffers"].insert( buffer_id_str ); - } - else if( source->get_type()->has_property( GateTypeProperty::ff ) ) - { - if( std::find( toggle_ffs.begin(), toggle_ffs.end(), source ) != toggle_ffs.end() ) - { - insert_edge( edges, source, reference ); - } - - continue; - } - else if( source->get_type()->has_property( GateTypeProperty::latch ) ) - { - continue; - } - - visited.insert( pair ); - - for( const Endpoint *ep : source->get_fan_in_endpoints() ) - { - const GatePin *pin = ep->get_pin(); - const PinType pin_type = pin->get_type(); - - if( is_control_pin( pin_type ) ) - { - // Don't traverse control signals of clock gates - continue; - } - - const Net *net = ep->get_net(); - const u32 net_id = net->get_id(); - const std::string net_name = net->get_name(); - - if( net_name == "'0'" || net_name == "'1'" ) - { - continue; - } - - if( net->is_global_input_net() ) - { - insert_edge( edges, net, reference ); - - vertices["global_inputs"].insert( net_name ); - continue; - } - - const u32 num_net_sources = net->get_num_of_sources(); - - if( num_net_sources == 0 ) - { - log_warning( "clock_tree_extractor", "unrouted clock net with ID {} ignored", net_id ); - continue; - } - else if( num_net_sources > 1 ) - { - log_warning( "clock_tree_extractor", "multi-driven clock net with ID {} ignored", net_id ); - continue; - } - - const std::vector net_sources = net->get_sources(); - const Endpoint *source_ep = net_sources.front(); - const Gate *gate = source_ep->get_gate(); - - if( auto it = visited.find( { reference, gate } ); it == visited.end() ) - { - queue.push( { reference, gate } ); - } - } - } - - export_clock_tree( vertices, edges, pathname ); - - return OK( edges.size() ); - } - - const std::vector> ClockTreeExtractor::get_edges() const - { - return m_edges; - } - } // namespace cte -} // namespace hal From 2164d558f66c9ae27079a59799d1f57166aca279 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Mon, 24 Nov 2025 15:04:41 -0500 Subject: [PATCH 10/23] start to implement methods to query clock tree --- .../include/clock_tree_extractor/clock_tree.h | 21 +- .../python/python_bindings.cpp | 37 +++ .../clock_tree_extractor/src/clock_tree.cpp | 216 ++++++++++++++++-- 3 files changed, 254 insertions(+), 20 deletions(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index e9872054e90d..bcf639ded2ec 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -26,6 +26,7 @@ #pragma once +#include "graph_algorithm/netlist_graph.h" #include "hal_core/defines.h" #include "hal_core/utilities/result.h" @@ -56,22 +57,32 @@ namespace hal { namespace cte { + enum PtrType { UNKNOWN, GATE, NET }; + class ClockTree { public: + ClockTree( const Netlist *netlist, + igraph_t &&graph, + std::unordered_set &&roots, + std::unordered_map &&m_vertices_to_ptrs, + std::unordered_map &&m_ptrs_to_types ); + ~ClockTree(); static Result> from_netlist( const Netlist *netlist ); Result export_dot( const std::string &pathname ) const; - const Gate *get_gate_from_vertex( igraph_integer_t vertex ) const; + Result> get_subtree( const void *ptr ) const; + + Result get_vertex_from_ptr( const void *ptr ) const; - const igraph_integer_t get_vertex_from_gate( const Gate *gate ) const; + const std::vector get_gates() const; - const Net *get_net_from_vertex( igraph_integer_t vertex ) const; + const std::vector get_nets() const; - const igraph_integer_t get_vertex_from_net( const Net *net ) const; + const std::unordered_map get_all() const; const Netlist *get_netlist() const; @@ -94,7 +105,7 @@ namespace hal std::unordered_map m_ptrs_to_vertices; - std::unordered_map m_ptrs_to_types; + std::unordered_map m_ptrs_to_types; }; } // namespace cte } // namespace hal diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index e64338093c76..c796b4aa2349 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -125,6 +125,43 @@ namespace hal }, py::arg( "pathname" ), R"()" ) + .def( + "get_subtree", + []( const cte::ClockTree &self, const void *ptr ) -> std::unique_ptr { + auto result = self.get_subtree( ptr ); + if( result.is_ok() ) + { + return result.get(); + } + + log_error( "clock_tree_extractor", "{}", result.get_error().get() ); + return nullptr; + }, + py::arg( "ptr" ), + py::return_value_policy::move, + R"()" ) + .def( + "get_all", + []( const cte::ClockTree &self ) -> py::list { + py::list result; + const auto &map = self.get_all(); + for( auto &[ptr, type] : map ) + { + if( type == cte::PtrType::GATE ) + { + result.append( py::cast( (const Gate *) ptr ) ); + } + else if( type == cte::PtrType::NET ) + { + result.append( py::cast( (const Net *) ptr ) ); + } + } + return result; + }, + R"()" ) + .def( "get_vertex_from_ptr", &cte::ClockTree::get_vertex_from_ptr, R"()" ) + .def( "get_gates", &cte::ClockTree::get_gates, R"()" ) + .def( "get_nets", &cte::ClockTree::get_nets, R"()" ) .def( "get_netlist", &cte::ClockTree::get_netlist, R"()" ); #ifndef PYBIND11_MODULE diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index f31510a54262..91af9837f4a6 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -105,6 +105,12 @@ namespace hal return result; } + + igraph_error_t + in_callback( const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra ) + { + return igraph_vector_int_push_back( (igraph_vector_int_t *) extra, vid ); + } } // namespace ClockTree::ClockTree( const Netlist *netlist ) @@ -113,6 +119,25 @@ namespace hal { } + ClockTree::ClockTree( const Netlist *netlist, + igraph_t &&igraph, + std::unordered_set &&roots, + std::unordered_map &&vertices_to_ptrs, + std::unordered_map &&ptrs_to_types ) + : m_netlist( netlist ) + , m_igraph( std::move( igraph ) ) + , m_roots( std::move( roots ) ) + , m_vertices_to_ptrs( std::move( vertices_to_ptrs ) ) + , m_ptrs_to_types( std::move( ptrs_to_types ) ) + { + m_igraph_ptr = &m_igraph; + + for( const auto &[vertex, ptr] : m_vertices_to_ptrs ) + { + m_ptrs_to_vertices[ptr] = vertex; + } + } + ClockTree::~ClockTree() { igraph_destroy( &m_igraph ); @@ -127,7 +152,7 @@ namespace hal std::unordered_set vertices; std::unordered_set, VoidPtrHash> edges; - std::unordered_map ptrs_to_type; + std::unordered_map ptrs_to_type; std::queue> queue; NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *netlist ); @@ -165,7 +190,7 @@ namespace hal else if( clk->is_global_input_net() ) { vertices.insert( (void *) clk ); - ptrs_to_type[(void *) clk] = "net"; + ptrs_to_type[(void *) clk] = PtrType::NET; continue; } else if( clk->get_num_of_sources() == 0 ) @@ -176,7 +201,7 @@ namespace hal queue.push( { ff, clk->get_sources().front()->get_gate() } ); vertices.insert( (void *) ff ); - ptrs_to_type[(void *) ff] = "gate"; + ptrs_to_type[(void *) ff] = PtrType::GATE; } const std::unordered_set toggle_ffs = get_toggle_ffs( netlist ); @@ -205,8 +230,8 @@ namespace hal vertices.insert( (void *) source ); vertices.insert( (void *) reference ); - ptrs_to_type[(void *) source] = "gate"; - ptrs_to_type[(void *) reference] = "gate"; + ptrs_to_type[(void *) source] = PtrType::GATE; + ptrs_to_type[(void *) reference] = PtrType::GATE; edges.insert( { (void *) source, (void *) reference } ); @@ -240,8 +265,8 @@ namespace hal vertices.insert( (void *) net ); vertices.insert( (void *) reference ); - ptrs_to_type[(void *) net] = "net"; - ptrs_to_type[(void *) reference] = "gate"; + ptrs_to_type[(void *) net] = PtrType::NET; + ptrs_to_type[(void *) reference] = PtrType::GATE; edges.insert( { (void *) net, (void *) reference } ); continue; @@ -347,7 +372,7 @@ namespace hal for( const auto &[ptr, vertex] : m_ptrs_to_vertices ) { - if( m_ptrs_to_types.at( ptr ) == "net" ) + if( m_ptrs_to_types.at( ptr ) == PtrType::NET ) { dot_fd << " " << ( (Net *) ptr )->get_name() << " [shape=circle];\n"; continue; @@ -400,9 +425,9 @@ namespace hal visited.insert( vertex ); const void *sptr = m_vertices_to_ptrs.at( vertex ); - const std::string stype = m_ptrs_to_types.at( sptr ); + const PtrType stype = m_ptrs_to_types.at( sptr ); - if( stype == "gate" && is_inverter( (Gate *) sptr ) ) + if( stype == PtrType::GATE && is_inverter( (Gate *) sptr ) ) { edge_color = edge_color == "red" ? "blue" : "red"; } @@ -423,13 +448,13 @@ namespace hal for( igraph_integer_t idx = 0; idx < igraph_vector_int_size( &neighbors ); idx++ ) { - const std::string src_id = - stype == "gate" ? std::to_string( ( (Gate *) sptr )->get_id() ) : ( (Net *) sptr )->get_name(); + const std::string src_id = stype == PtrType::GATE ? std::to_string( ( (Gate *) sptr )->get_id() ) + : ( (Net *) sptr )->get_name(); const void *dptr = m_vertices_to_ptrs.at( VECTOR( neighbors )[idx] ); - const std::string dtype = m_ptrs_to_types.at( dptr ); - const std::string dst_id = - dtype == "gate" ? std::to_string( ( (Gate *) dptr )->get_id() ) : ( (Net *) dptr )->get_name(); + const PtrType dtype = m_ptrs_to_types.at( dptr ); + const std::string dst_id = dtype == PtrType::GATE ? std::to_string( ( (Gate *) dptr )->get_id() ) + : ( (Net *) dptr )->get_name(); dot_fd << " " << src_id << " -> " << dst_id << " [color=" << edge_color << "];\n"; queue.push( { VECTOR( neighbors )[idx], edge_color } ); @@ -444,6 +469,164 @@ namespace hal return OK( {} ); } + Result> ClockTree::get_subtree( const void *ptr ) const + { + auto it = m_ptrs_to_vertices.find( ptr ); + if( it == m_ptrs_to_vertices.end() ) + { + return ERR( "object is not part of clock tree" ); + } + + const igraph_integer_t root = it->second; + + igraph_error_t ierror; + igraph_vector_int_t vertices; + if( ( ierror = igraph_vector_int_init( &vertices, 0 ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + if( ( ierror = igraph_dfs( m_igraph_ptr, + root, + IGRAPH_OUT, + false, + nullptr, + nullptr, + nullptr, + nullptr, + in_callback, + nullptr, + &vertices ) ) + != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &vertices ); + return ERR( igraph_strerror( ierror ) ); + } + + igraph_vs_t vs; + if( ( ierror = igraph_vs_vector( &vs, &vertices ) ) != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &vertices ); + return ERR( igraph_strerror( ierror ) ); + } + + igraph_vector_int_t map; + if( ( ierror = igraph_vector_int_init( &map, igraph_vcount( m_igraph_ptr ) ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + igraph_t igraph; + if( ( ierror = + igraph_induced_subgraph_map( m_igraph_ptr, &igraph, vs, IGRAPH_SUBGRAPH_AUTO, &map, nullptr ) ) + != IGRAPH_SUCCESS ) + { + igraph_vs_destroy( &vs ); + igraph_vector_int_destroy( &map ); + igraph_vector_int_destroy( &vertices ); + return ERR( igraph_strerror( ierror ) ); + } + + igraph_vs_destroy( &vs ); + igraph_vector_int_destroy( &vertices ); + + std::unordered_set roots; + std::unordered_map ptrs_to_types; + std::unordered_map vertices_to_ptrs; + + for( igraph_integer_t idx = 0; idx < igraph_vector_int_size( &map ); idx++ ) + { + const igraph_integer_t vertex = VECTOR( map )[idx]; + if( vertex == 0 ) + { + continue; + } + + const void *ptr = m_vertices_to_ptrs.at( idx ); + + vertices_to_ptrs[vertex - 1] = ptr; + ptrs_to_types[ptr] = m_ptrs_to_types.at( ptr ); + } + + igraph_vector_int_destroy( &map ); + + igraph_vector_int_t indegrees; + if( ( ierror = igraph_vector_int_init( &indegrees, igraph_vcount( &igraph ) ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + if( ( ierror = igraph_degree( &igraph, &indegrees, igraph_vss_all(), IGRAPH_IN, IGRAPH_NO_LOOPS ) ) + != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &indegrees ); + return ERR( igraph_strerror( ierror ) ); + } + + for( igraph_integer_t idx = 0; idx < igraph_vector_int_size( &indegrees ); idx++ ) + { + if( VECTOR( indegrees )[idx] != 0 ) + { + continue; + } + roots.insert( idx ); + } + + igraph_vector_int_destroy( &indegrees ); + + return OK( std::make_unique( m_netlist, + std::move( igraph ), + std::move( roots ), + std::move( vertices_to_ptrs ), + std::move( ptrs_to_types ) ) ); + } + + Result ClockTree::get_vertex_from_ptr( const void *ptr ) const + { + auto it = m_ptrs_to_vertices.find( ptr ); + if( it == m_ptrs_to_vertices.end() ) + { + return ERR( "object is not part of clock tree" ); + } + + return OK( it->second ); + } + + const std::vector ClockTree::get_gates() const + { + std::vector result; + + for( const auto &[ptr, type] : m_ptrs_to_types ) + { + if( type == PtrType::GATE ) + { + result.push_back( (const Gate *) ptr ); + } + } + + return result; + } + + const std::vector ClockTree::get_nets() const + { + std::vector result; + + for( const auto &[ptr, type] : m_ptrs_to_types ) + { + if( type == PtrType::NET ) + { + result.push_back( (const Net *) ptr ); + } + } + + return result; + } + + const std::unordered_map ClockTree::get_all() const + { + return m_ptrs_to_types; + } + const Netlist *ClockTree::get_netlist() const { return m_netlist; @@ -458,3 +641,6 @@ namespace hal // BUG: looks like ~20 vertices plus their edges are missing in benchmark // TODO: investigate possible BUG + +// BUG: subtree is wrong +// TODO: investigate why? Probably mapping is broken \ No newline at end of file From 1d6355a2ff01e05aca99bdf60d909126fb2cfccb Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Wed, 26 Nov 2025 14:25:00 -0500 Subject: [PATCH 11/23] allow to retrieve subtree of parent --- .../include/clock_tree_extractor/clock_tree.h | 2 +- .../python/python_bindings.cpp | 7 ++-- .../clock_tree_extractor/src/clock_tree.cpp | 34 ++++++++++++++----- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index bcf639ded2ec..1d694fd771c4 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -74,7 +74,7 @@ namespace hal Result export_dot( const std::string &pathname ) const; - Result> get_subtree( const void *ptr ) const; + Result> get_subtree( const void *ptr, const bool parent ) const; Result get_vertex_from_ptr( const void *ptr ) const; diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index c796b4aa2349..ac4ec4294ade 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -127,8 +127,10 @@ namespace hal R"()" ) .def( "get_subtree", - []( const cte::ClockTree &self, const void *ptr ) -> std::unique_ptr { - auto result = self.get_subtree( ptr ); + []( const cte::ClockTree &self, + const void *ptr, + const bool parent ) -> std::unique_ptr { + auto result = self.get_subtree( ptr, parent ); if( result.is_ok() ) { return result.get(); @@ -138,6 +140,7 @@ namespace hal return nullptr; }, py::arg( "ptr" ), + py::arg( "parent" ) = false, py::return_value_policy::move, R"()" ) .def( diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index 91af9837f4a6..fd69e085216b 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -469,7 +469,7 @@ namespace hal return OK( {} ); } - Result> ClockTree::get_subtree( const void *ptr ) const + Result> ClockTree::get_subtree( const void *ptr, const bool parent ) const { auto it = m_ptrs_to_vertices.find( ptr ); if( it == m_ptrs_to_vertices.end() ) @@ -477,9 +477,31 @@ namespace hal return ERR( "object is not part of clock tree" ); } - const igraph_integer_t root = it->second; - igraph_error_t ierror; + igraph_integer_t root = it->second; + if( parent ) + { + igraph_vector_int_t parents; + if( ( ierror = igraph_vector_int_init( &parents, 0 ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + if( ( ierror = igraph_neighbors( m_igraph_ptr, &parents, root, IGRAPH_IN ) ) != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &parents ); + return ERR( igraph_strerror( ierror ) ); + } + + // Only accept, if there is only one parent vertex for now. + if( igraph_vector_int_size( &parents ) == 1 ) + { + root = VECTOR( parents )[0]; + } + + igraph_vector_int_destroy( &parents ); + } + igraph_vector_int_t vertices; if( ( ierror = igraph_vector_int_init( &vertices, 0 ) ) != IGRAPH_SUCCESS ) { @@ -638,9 +660,3 @@ namespace hal } } // namespace cte } // namespace hal - -// BUG: looks like ~20 vertices plus their edges are missing in benchmark -// TODO: investigate possible BUG - -// BUG: subtree is wrong -// TODO: investigate why? Probably mapping is broken \ No newline at end of file From db19f291ebc148ad1f9ee9c7051e3cca4527ef0a Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Thu, 27 Nov 2025 09:19:01 -0500 Subject: [PATCH 12/23] user can now convert vertices to ptrs and vice versa --- .../include/clock_tree_extractor/clock_tree.h | 7 ++ .../python/python_bindings.cpp | 79 ++++++++++++++++++- .../clock_tree_extractor/src/clock_tree.cpp | 52 ++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index 1d694fd771c4..7c7caa734177 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -78,6 +78,13 @@ namespace hal Result get_vertex_from_ptr( const void *ptr ) const; + Result> get_ptr_from_vertex( const igraph_integer_t vertex ) const; + + Result> get_vertices_from_ptrs( const std::vector &ptrs ) const; + + Result>> + get_ptrs_from_vertices( const std::vector &vertices ) const; + const std::vector get_gates() const; const std::vector get_nets() const; diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index ac4ec4294ade..eb19045b6dcd 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -162,7 +162,84 @@ namespace hal return result; }, R"()" ) - .def( "get_vertex_from_ptr", &cte::ClockTree::get_vertex_from_ptr, R"()" ) + .def( + "get_vertex_from_ptr", + []( const cte::ClockTree &self, const void *ptr ) -> py::object { + auto result = self.get_vertex_from_ptr( ptr ); + if( result.is_ok() ) + { + return py::int_( result.get() ); + } + log_error( "clock_tree_extractor", "{}", result.get_error().get() ); + return py::none(); + }, + py::arg( "ptr" ), + R"()" ) + .def( + "get_ptr_from_vertex", + []( const cte::ClockTree &self, const igraph_integer_t vertex ) -> py::object { + auto result = self.get_ptr_from_vertex( vertex ); + if( result.is_ok() ) + { + auto [ptr, type] = result.get(); + if( type == cte::PtrType::GATE ) + { + return py::cast( (const Gate *) ptr ); + } + else if( type == cte::PtrType::NET ) + { + return py::cast( (const Net *) ptr ); + } + return py::none(); + } + log_error( "clock_tree_extractor", "{}", result.get_error().get() ); + return py::none(); + }, + py::arg( "vertex" ), + R"()" ) + .def( + "get_vertices_from_ptrs", + []( const cte::ClockTree &self, const std::vector &ptrs ) -> py::list { + auto result = self.get_vertices_from_ptrs( ptrs ); + if( result.is_ok() ) + { + return py::cast( result.get() ); + } + log_error( "clock_tree_extractor", "{}", result.get_error().get() ); + return py::none(); + }, + py::arg( "ptrs" ), + R"()" ) + .def( + "get_ptrs_from_vertices", + []( const cte::ClockTree &self, const std::vector &vertices ) -> py::list { + auto res = self.get_ptrs_from_vertices( vertices ); + if( res.is_ok() ) + { + py::list result; + for( const auto &[ptr, type] : res.get() ) + { + if( type == cte::PtrType::GATE ) + { + result.append( py::cast( (const Gate *) ptr ) ); + } + else if( type == cte::PtrType::NET ) + { + result.append( py::cast( (const Net *) ptr ) ); + } + else + { + log_error( "clock_tree_extractor", "unknown ptr type" ); + return py::none(); + } + } + return result; + } + log_error( "clock_tree_extractor", "{}", res.get_error().get() ); + return py::none(); + }, + py::arg( "vertices" ), + R"()" ) .def( "get_gates", &cte::ClockTree::get_gates, R"()" ) .def( "get_nets", &cte::ClockTree::get_nets, R"()" ) .def( "get_netlist", &cte::ClockTree::get_netlist, R"()" ); diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index fd69e085216b..3266c0caea90 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -195,6 +195,9 @@ namespace hal } else if( clk->get_num_of_sources() == 0 ) { + log_warning( "clock_tree_extractor", + "unrouted clock net with ID {} ignored", + std::to_string( clk->get_id() ) ); continue; } @@ -614,6 +617,55 @@ namespace hal return OK( it->second ); } + Result> ClockTree::get_ptr_from_vertex( const igraph_integer_t vertex ) const + { + auto it = m_vertices_to_ptrs.find( vertex ); + if( it == m_vertices_to_ptrs.end() ) + { + return ERR( "object is not part of clock tree" ); + } + + return OK( std::make_pair( it->second, m_ptrs_to_types.at( it->second ) ) ); + } + + Result> + ClockTree::get_vertices_from_ptrs( const std::vector &ptrs ) const + { + std::vector result; + + for( const void *ptr : ptrs ) + { + auto res = get_vertex_from_ptr( ptr ); + if( res.is_error() ) + { + return ERR( res.get_error().get() ); + } + + result.push_back( res.get() ); + } + + return OK( result ); + } + + Result>> + ClockTree::get_ptrs_from_vertices( const std::vector &vertices ) const + { + std::vector> result; + + for( const igraph_integer_t vertex : vertices ) + { + auto res = get_ptr_from_vertex( vertex ); + if( res.is_error() ) + { + return ERR( res.get_error().get() ); + } + + result.push_back( res.get() ); + } + + return OK( result ); + } + const std::vector ClockTree::get_gates() const { std::vector result; From abc94cb0985d6637abebe129a14967d7fb05e2b0 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Wed, 28 Jan 2026 16:03:28 -0500 Subject: [PATCH 13/23] extend api for bt --- .../include/clock_tree_extractor/clock_tree.h | 27 ++++++ .../python/python_bindings.cpp | 80 +++++++++++++++++ .../clock_tree_extractor/src/clock_tree.cpp | 87 ++++++++++++++----- 3 files changed, 170 insertions(+), 24 deletions(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index 7c7caa734177..fcb950e9cd66 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -59,6 +59,23 @@ namespace hal { enum PtrType { UNKNOWN, GATE, NET }; + struct VoidPtrHash + { + std::size_t operator()( const std::pair &pair ) const noexcept + { + return std::hash()( pair.first ) ^ ( std::hash()( pair.second ) << 1 ); + } + }; + + struct PairPtrEq + { + bool operator()( const std::pair &p1, + const std::pair &p2 ) const noexcept + { + return p1.first == p2.first && p1.second == p2.second; + } + }; + class ClockTree { public: @@ -74,6 +91,9 @@ namespace hal Result export_dot( const std::string &pathname ) const; + Result>> get_neighbors( const void *ptr, + igraph_neimode_t direction ) const; + Result> get_subtree( const void *ptr, const bool parent ) const; Result get_vertex_from_ptr( const void *ptr ) const; @@ -85,6 +105,10 @@ namespace hal Result>> get_ptrs_from_vertices( const std::vector &vertices ) const; + const std:: + unordered_map, std::vector, VoidPtrHash, PairPtrEq> + get_paths() const; + const std::vector get_gates() const; const std::vector get_nets() const; @@ -113,6 +137,9 @@ namespace hal std::unordered_map m_ptrs_to_vertices; std::unordered_map m_ptrs_to_types; + + std::unordered_map, std::vector, VoidPtrHash, PairPtrEq> + m_paths; }; } // namespace cte } // namespace hal diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index eb19045b6dcd..931f4b711caa 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -240,6 +240,86 @@ namespace hal }, py::arg( "vertices" ), R"()" ) + .def( + "get_paths", + []( const cte::ClockTree &self ) -> py::dict { + auto res = self.get_paths(); + py::dict result; + for( const auto &[key, val] : res ) + { + py::tuple py_key( 2 ); + py_key[0] = py::cast( (const Gate *) key.first ); + py_key[1] = py::cast( (const Gate *) key.second ); + py::list py_val; + for( const Gate *gate : val ) + { + py_val.append( gate ); + } + result[py_key] = py_val; + } + return result; + }, + R"()" ) + .def( + "get_parents", + []( const cte::ClockTree &self, const void *ptr ) -> py::list { + auto res = self.get_neighbors( ptr, IGRAPH_IN ); + if( res.is_ok() ) + { + py::list result; + for( const auto &[ptr, type] : res.get() ) + { + if( type == cte::PtrType::GATE ) + { + result.append( py::cast( (const Gate *) ptr ) ); + } + else if( type == cte::PtrType::NET ) + { + result.append( py::cast( (const Net *) ptr ) ); + } + else + { + log_error( "clock_tree_extractor", "unknown ptr type" ); + return py::none(); + } + } + return result; + } + log_error( "clock_tree_extractor", "{}", res.get_error().get() ); + return py::none(); + }, + py::arg( "ptr" ), + R"()" ) + .def( + "get_childs", + []( const cte::ClockTree &self, const void *ptr ) -> py::list { + auto res = self.get_neighbors( ptr, IGRAPH_OUT ); + if( res.is_ok() ) + { + py::list result; + for( const auto &[ptr, type] : res.get() ) + { + if( type == cte::PtrType::GATE ) + { + result.append( py::cast( (const Gate *) ptr ) ); + } + else if( type == cte::PtrType::NET ) + { + result.append( py::cast( (const Net *) ptr ) ); + } + else + { + log_error( "clock_tree_extractor", "unknown ptr type" ); + return py::none(); + } + } + return result; + } + log_error( "clock_tree_extractor", "{}", res.get_error().get() ); + return py::none(); + }, + py::arg( "ptr" ), + R"()" ) .def( "get_gates", &cte::ClockTree::get_gates, R"()" ) .def( "get_nets", &cte::ClockTree::get_nets, R"()" ) .def( "get_netlist", &cte::ClockTree::get_netlist, R"()" ); diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index 3266c0caea90..eb4aaf330f57 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -26,23 +26,6 @@ namespace hal { namespace { - struct VoidPtrHash - { - std::size_t operator()( const std::pair &pair ) const - { - return std::hash()( pair.first ) ^ ( std::hash()( pair.second ) << 1 ); - } - }; - - struct PairPtrEq - { - bool operator()( const std::pair &p1, - const std::pair &p2 ) const noexcept - { - return p1.first == p2.first && p1.second == p2.second; - } - }; - inline bool is_ff( const Gate *gate ) { return gate->get_type()->has_property( GateTypeProperty::ff ); @@ -154,10 +137,13 @@ namespace hal std::unordered_set, VoidPtrHash> edges; std::unordered_map ptrs_to_type; - std::queue> queue; + std::queue>> queue; NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *netlist ); std::unordered_set, VoidPtrHash> visited; + std::unordered_map, std::vector, VoidPtrHash, PairPtrEq> + paths; + for( const Gate *ff : netlist->get_gates( is_ff ) ) { const std::vector clock_pins = ff->get_type()->get_pins( []( const auto &p ) { @@ -201,7 +187,7 @@ namespace hal continue; } - queue.push( { ff, clk->get_sources().front()->get_gate() } ); + queue.push( { ff, clk->get_sources().front()->get_gate(), std::vector{} } ); vertices.insert( (void *) ff ); ptrs_to_type[(void *) ff] = PtrType::GATE; @@ -211,11 +197,14 @@ namespace hal while( !queue.empty() ) { - const std::pair pair = queue.front(); + const std::tuple> tuple = queue.front(); queue.pop(); - Gate *source = (Gate *) pair.second; - Gate *reference = (Gate *) pair.first; + Gate *source = (Gate *) std::get<1>( tuple ); + Gate *reference = (Gate *) std::get<0>( tuple ); + std::vector path = std::get<2>( tuple ); + + path.push_back( source ); if( is_latch( source ) ) { @@ -237,6 +226,9 @@ namespace hal ptrs_to_type[(void *) reference] = PtrType::GATE; edges.insert( { (void *) source, (void *) reference } ); + path.push_back( reference ); + paths[{ (void *) source, (void *) reference }] = path; + path.clear(); if( is_ff( source ) ) { @@ -246,7 +238,7 @@ namespace hal reference = (Gate *) source; } - visited.insert( pair ); + visited.insert( std::make_pair( reference, source ) ); for( const Endpoint *ep : source->get_fan_in_endpoints() ) { @@ -272,6 +264,10 @@ namespace hal ptrs_to_type[(void *) reference] = PtrType::GATE; edges.insert( { (void *) net, (void *) reference } ); + + // paths[{ (void *) net, (void *) reference }] = path; + // path.clear(); + continue; } @@ -293,7 +289,7 @@ namespace hal const Gate *new_source = net->get_sources().front()->get_gate(); if( visited.find( { reference, new_source } ) == visited.end() ) { - queue.push( { reference, new_source } ); + queue.push( { reference, new_source, path } ); } } } @@ -310,6 +306,7 @@ namespace hal } clock_tree->m_ptrs_to_types = ptrs_to_type; + clock_tree->m_paths = std::move( paths ); igraph_error_t ierror; igraph_vector_int_t iedges; @@ -710,5 +707,47 @@ namespace hal { return m_igraph_ptr; } + + const std:: + unordered_map, std::vector, VoidPtrHash, PairPtrEq> + ClockTree::get_paths() const + { + return m_paths; + } + + Result>> + ClockTree::get_neighbors( const void *ptr, igraph_neimode_t direction ) const + { + auto it = m_ptrs_to_vertices.find( ptr ); + if( it == m_ptrs_to_vertices.end() ) + { + return ERR( "object is not part of clock tree" ); + } + + igraph_error_t ierror; + igraph_vector_int_t neighbors; + + if( ( ierror = igraph_vector_int_init( &neighbors, 0 ) ) != IGRAPH_SUCCESS ) + { + return ERR( igraph_strerror( ierror ) ); + } + + if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, it->second, direction ) ) != IGRAPH_SUCCESS ) + { + igraph_vector_int_destroy( &neighbors ); + return ERR( igraph_strerror( ierror ) ); + } + + std::vector> result; + for( igraph_integer_t idx = 0; idx < igraph_vector_int_size( &neighbors ); idx++ ) + { + const void *n_ptr = m_vertices_to_ptrs.at( VECTOR( neighbors )[idx] ); + result.push_back( std::make_pair( n_ptr, m_ptrs_to_types.at( n_ptr ) ) ); + } + + igraph_vector_int_destroy( &neighbors ); + + return OK( result ); + } } // namespace cte } // namespace hal From bdd88da91f7643c04f4d41715566ee47ed317d08 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Mon, 2 Feb 2026 09:54:39 -0500 Subject: [PATCH 14/23] bugfix: invalid paths --- plugins/clock_tree_extractor/src/clock_tree.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index eb4aaf330f57..6995820a71af 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -187,7 +187,7 @@ namespace hal continue; } - queue.push( { ff, clk->get_sources().front()->get_gate(), std::vector{} } ); + queue.push( { ff, clk->get_sources().front()->get_gate(), std::vector{ ff } } ); vertices.insert( (void *) ff ); ptrs_to_type[(void *) ff] = PtrType::GATE; @@ -226,9 +226,9 @@ namespace hal ptrs_to_type[(void *) reference] = PtrType::GATE; edges.insert( { (void *) source, (void *) reference } ); - path.push_back( reference ); paths[{ (void *) source, (void *) reference }] = path; path.clear(); + path.push_back( source ); if( is_ff( source ) ) { From 25b5943d735fdd03f6c9c6a7dc2a6e71a0d5f31d Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Mon, 23 Feb 2026 11:27:16 -0500 Subject: [PATCH 15/23] allow the algorithm to also extract meshes --- .../clock_tree_extractor/src/clock_tree.cpp | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index 6995820a71af..9a5718fb8fbc 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -169,9 +169,26 @@ namespace hal if( clk->get_num_of_sources() > 1 ) { - log_error( "clock_tree_extractor", - "invalid number of sources for clock net with ID " + std::to_string( clk->get_id() ) ); - continue; + bool valid = true; + for( const Endpoint *source_ep : clk->get_sources() ) + { + const Gate *gate = source_ep->get_gate(); + if( !( is_buffer(gate) || is_inverter(gate) ) ) + { + // In theory, it should be either all buffers or all inverters. But depending on + // extraction results, e.g., it could happen that a buffer is split into two inverters. + // So I just assume it is fine if all the sources are either a buffer or an inverter + // without enforcing strict buffer only or inverter only. + valid = false; + break; + } + } + if( !valid ) + { + log_error( "clock_tree_extractor", + "invalid number of sources for clock net with ID " + std::to_string( clk->get_id() ) ); + continue; + } } else if( clk->is_global_input_net() ) { @@ -187,7 +204,11 @@ namespace hal continue; } - queue.push( { ff, clk->get_sources().front()->get_gate(), std::vector{ ff } } ); + for( const Endpoint *source_ep : clk->get_sources() ) + { + const Gate *gate = source_ep->get_gate(); + queue.push( { ff, gate, std::vector{ ff } } ); + } vertices.insert( (void *) ff ); ptrs_to_type[(void *) ff] = PtrType::GATE; From e47a62bb03e7ae25688c6f0b35d4ec590ec5eada Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 5 Jun 2026 11:37:58 -0400 Subject: [PATCH 16/23] include clock gates in clock tree --- .../include/clock_tree_extractor/clock_tree.h | 7 --- .../python/python_bindings.cpp | 20 ------- .../clock_tree_extractor/src/clock_tree.cpp | 55 +++++++++++-------- 3 files changed, 31 insertions(+), 51 deletions(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index fcb950e9cd66..2b99a7de0c08 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -105,10 +105,6 @@ namespace hal Result>> get_ptrs_from_vertices( const std::vector &vertices ) const; - const std:: - unordered_map, std::vector, VoidPtrHash, PairPtrEq> - get_paths() const; - const std::vector get_gates() const; const std::vector get_nets() const; @@ -137,9 +133,6 @@ namespace hal std::unordered_map m_ptrs_to_vertices; std::unordered_map m_ptrs_to_types; - - std::unordered_map, std::vector, VoidPtrHash, PairPtrEq> - m_paths; }; } // namespace cte } // namespace hal diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index 931f4b711caa..1e256d5b1516 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -240,26 +240,6 @@ namespace hal }, py::arg( "vertices" ), R"()" ) - .def( - "get_paths", - []( const cte::ClockTree &self ) -> py::dict { - auto res = self.get_paths(); - py::dict result; - for( const auto &[key, val] : res ) - { - py::tuple py_key( 2 ); - py_key[0] = py::cast( (const Gate *) key.first ); - py_key[1] = py::cast( (const Gate *) key.second ); - py::list py_val; - for( const Gate *gate : val ) - { - py_val.append( gate ); - } - result[py_key] = py_val; - } - return result; - }, - R"()" ) .def( "get_parents", []( const cte::ClockTree &self, const void *ptr ) -> py::list { diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index 9a5718fb8fbc..d71c14641fa4 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -141,9 +141,6 @@ namespace hal NetlistTraversalDecorator ntd = NetlistTraversalDecorator( *netlist ); std::unordered_set, VoidPtrHash> visited; - std::unordered_map, std::vector, VoidPtrHash, PairPtrEq> - paths; - for( const Gate *ff : netlist->get_gates( is_ff ) ) { const std::vector clock_pins = ff->get_type()->get_pins( []( const auto &p ) { @@ -173,7 +170,7 @@ namespace hal for( const Endpoint *source_ep : clk->get_sources() ) { const Gate *gate = source_ep->get_gate(); - if( !( is_buffer(gate) || is_inverter(gate) ) ) + if( !( is_buffer( gate ) || is_inverter( gate ) ) ) { // In theory, it should be either all buffers or all inverters. But depending on // extraction results, e.g., it could happen that a buffer is split into two inverters. @@ -186,7 +183,8 @@ namespace hal if( !valid ) { log_error( "clock_tree_extractor", - "invalid number of sources for clock net with ID " + std::to_string( clk->get_id() ) ); + "invalid number of sources for clock net with ID " + + std::to_string( clk->get_id() ) ); continue; } } @@ -240,14 +238,17 @@ namespace hal continue; } - vertices.insert( (void *) source ); - vertices.insert( (void *) reference ); + for( const Gate *gate : path ) + { + vertices.insert( (void *) gate ); + ptrs_to_type[(void *) gate] = PtrType::GATE; + } - ptrs_to_type[(void *) source] = PtrType::GATE; - ptrs_to_type[(void *) reference] = PtrType::GATE; + for( u32 idx = 0; idx < path.size() - 1; idx++ ) + { + edges.insert( { (void *) path[idx + 1], (void *) path[idx] } ); + } - edges.insert( { (void *) source, (void *) reference } ); - paths[{ (void *) source, (void *) reference }] = path; path.clear(); path.push_back( source ); @@ -278,16 +279,25 @@ namespace hal if( net->is_global_input_net() ) { + for( const Gate *gate : path ) + { + vertices.insert( (void *) gate ); + ptrs_to_type[(void *) gate] = PtrType::GATE; + } + + for( u32 idx = 0; idx < path.size() - 1; idx++ ) + { + edges.insert( { (void *) path[idx + 1], (void *) path[idx] } ); + } + vertices.insert( (void *) net ); - vertices.insert( (void *) reference ); ptrs_to_type[(void *) net] = PtrType::NET; - ptrs_to_type[(void *) reference] = PtrType::GATE; - edges.insert( { (void *) net, (void *) reference } ); + edges.insert( { (void *) net, (void *) path.back() } ); - // paths[{ (void *) net, (void *) reference }] = path; - // path.clear(); + path.clear(); + path.push_back( source ); continue; } @@ -327,7 +337,6 @@ namespace hal } clock_tree->m_ptrs_to_types = ptrs_to_type; - clock_tree->m_paths = std::move( paths ); igraph_error_t ierror; igraph_vector_int_t iedges; @@ -420,6 +429,11 @@ namespace hal dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << ( coords.size() == 0 ? ";\n" : " [" + coords + "];\n" ); } + else + { + dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << " [" << coords + << " shape=hexagon];\n"; + } } std::queue> queue; @@ -729,13 +743,6 @@ namespace hal return m_igraph_ptr; } - const std:: - unordered_map, std::vector, VoidPtrHash, PairPtrEq> - ClockTree::get_paths() const - { - return m_paths; - } - Result>> ClockTree::get_neighbors( const void *ptr, igraph_neimode_t direction ) const { From e9771d47c97d8fdd693ccb56bb9ace3d52e2a9af Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Sun, 5 Jul 2026 19:41:15 -0400 Subject: [PATCH 17/23] support delay gate types; fix missing unconnected flip-flops; add workaround for negative gate coordinates --- .../clock_tree_extractor/src/clock_tree.cpp | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index d71c14641fa4..121bd5455ba3 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -46,6 +46,11 @@ namespace hal return gate->get_type()->has_property( GateTypeProperty::c_inverter ); } + inline bool is_delay( const Gate *gate ) + { + return gate->get_type()->has_property( GateTypeProperty::delay ); + } + inline bool is_control_pin( const PinType &pin_type ) { return pin_type == PinType::clock || pin_type == PinType::enable || pin_type == PinType::select @@ -143,6 +148,9 @@ namespace hal for( const Gate *ff : netlist->get_gates( is_ff ) ) { + vertices.insert( (void *) ff ); + ptrs_to_type[(void *) ff] = PtrType::GATE; + const std::vector clock_pins = ff->get_type()->get_pins( []( const auto &p ) { return ( p->get_direction() == PinDirection::input ) && ( p->get_type() == PinType::clock ); } ); @@ -207,9 +215,6 @@ namespace hal const Gate *gate = source_ep->get_gate(); queue.push( { ff, gate, std::vector{ ff } } ); } - - vertices.insert( (void *) ff ); - ptrs_to_type[(void *) ff] = PtrType::GATE; } const std::unordered_set toggle_ffs = get_toggle_ffs( netlist ); @@ -230,7 +235,7 @@ namespace hal // Ignore latches continue; } - else if( is_buffer( source ) || is_inverter( source ) || is_ff( source ) ) + else if( is_buffer( source ) || is_inverter( source ) || is_delay( source ) || is_ff( source ) ) { if( is_ff( source ) && toggle_ffs.find( source ) == toggle_ffs.end() ) { @@ -408,32 +413,52 @@ namespace hal continue; } - const i32 x = ( (Gate *) ptr )->get_location_x(); - const i32 y = ( (Gate *) ptr )->get_location_y(); + const Gate *gate = (const Gate *) ptr; + + std::string coords = ""; + + // Workaround for negative coordinates - const std::string coords = - ( x < 0 || y < 0 ) ? "" : "x=" + std::to_string( x ) + " y=" + std::to_string( y ); + // const i32 x = gate->get_location_x(); + // const i32 y = gate->get_location_y(); - if( is_buffer( (Gate *) ptr ) ) + try { - dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << " [" << coords - << " shape=rectangle];\n"; + const i32 x = std::stoi( std::get<1>( gate->get_data( "generic", "X" ) ) ); + const i32 y = std::stoi( std::get<1>( gate->get_data( "generic", "Y" ) ) ); + coords = " x=" + std::to_string( x ) + " y=" + std::to_string( y ); + } catch( const std::invalid_argument &err ) + { + log_error( "clock_tree_extractor", "invalid coordinate format: {}", err.what() ); + } + + std::string shape = "shape=hexagon"; // default (clock gates) + + if( is_buffer( gate ) ) + { + shape = "shape=rectangle"; } - else if( is_inverter( (Gate *) ptr ) ) + else if( is_inverter( gate ) ) { - dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << " [" << coords - << " shape=triangle, orientation=180];\n"; + shape = "shape=triangle orientation=180"; } - else if( is_ff( (Gate *) ptr ) ) + else if( is_ff( gate ) ) { - dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) - << ( coords.size() == 0 ? ";\n" : " [" + coords + "];\n" ); + shape = ""; // no shape } - else + else if( is_delay( gate ) ) { - dot_fd << " " << std::to_string( ( (Gate *) ptr )->get_id() ) << " [" << coords - << " shape=hexagon];\n"; + shape = "shape=square"; } + + dot_fd << " " << gate->get_id() << " [instance=\"" << gate->get_name() << "\"" << coords; + + if( !shape.empty() ) + { + dot_fd << " " << shape; + } + + dot_fd << "];\n"; } std::queue> queue; From 6641841ed24e3e7b7f5b4a03d49138e20fe960e0 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Sun, 5 Jul 2026 20:29:27 -0400 Subject: [PATCH 18/23] add gate type as node attribute to dot export --- plugins/clock_tree_extractor/src/clock_tree.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index 121bd5455ba3..b1ff7ffd077c 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -451,7 +451,8 @@ namespace hal shape = "shape=square"; } - dot_fd << " " << gate->get_id() << " [instance=\"" << gate->get_name() << "\"" << coords; + dot_fd << " " << gate->get_id() << " [instance=\"" << gate->get_name() << "\" type=\"" + << gate->get_type()->get_name() << "\"" << coords; if( !shape.empty() ) { From 1a50e4e7d4b437097897469211af919a38ff2bd4 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 31 Jul 2026 17:52:41 -0400 Subject: [PATCH 19/23] add missing dependency for docker --- install_dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_dependencies.sh b/install_dependencies.sh index 8e8be3264c0e..caf948334530 100755 --- a/install_dependencies.sh +++ b/install_dependencies.sh @@ -115,6 +115,6 @@ elif [[ "$platform" == 'docker' ]]; then libqt5svg5-dev libqt5svg5* ninja-build lcov gcovr python3-sphinx \ doxygen python3-sphinx-rtd-theme python3-jedi python3-pip \ pybind11-dev python3-pybind11 python3-dateutil rapidjson-dev \ - libspdlog-dev libz3-dev libreadline-dev \ + libspdlog-dev libz3-dev libreadline-dev libgraphviz-dev \ graphviz libomp-dev libsuitesparse-dev # For documentation fi From ed836a8faa04571c5465c430c727599a4cc4e8e9 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Thu, 27 Aug 2026 23:32:06 -0400 Subject: [PATCH 20/23] update readme and copyright --- README.md | 1 + .../include/clock_tree_extractor/clock_tree.h | 4 +-- .../plugin_clock_tree_extractor.h | 4 +-- .../python/python_bindings.cpp | 28 ++++++++++++++++++- .../clock_tree_extractor/src/clock_tree.cpp | 26 +++++++++++++++++ .../src/plugin_clock_tree_extractor.cpp | 28 ++++++++++++++++++- 6 files changed, 85 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e6c26070b255..ed1f4c694cd3 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ This repository contains a selection of curated plugins: - Detailed widgets with information on all aspects of the inspected netlist - **Netlist Simulator:** A simulator for arbitrary parts of a loaded netlist - **Dataflow Analysis:** Our dataflow analysis plugin [DANA](https://eprint.iacr.org/2020/751.pdf) that recovers high-level registers in an unstructured netlist +- **Clock Tree Extractor:** A plugin to recover clock trees from an unstructured gate-level netlist - **Graph Algorithms:** [igraph](https://igraph.org) integration for direct access to common algorithms from graph-theory - **Python Shell:** A command-line plugin to spawn a Python shell preloaded with the HAL Python bindings - **VHDL & Verilog Parsers:** Adds support for parsing VHDL and Verilog files as netlist input formats diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index 2b99a7de0c08..cc2b44aea511 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -3,8 +3,8 @@ // 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. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// Copyright (c) 2026 Sascha Tommasone. 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 diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h index f2f7bcabeff6..de9e5a93980b 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h @@ -3,8 +3,8 @@ // 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. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// Copyright (c) 2026 Sascha Tommasone. 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 diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index 1e256d5b1516..10297805e129 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -1,3 +1,29 @@ +// 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. +// Copyright (c) 2026 Sascha Tommasone. 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. + #include "hal_core/python_bindings/python_bindings.h" #include "clock_tree_extractor/clock_tree.h" @@ -308,4 +334,4 @@ namespace hal return m.ptr(); #endif // PYBIND11_MODULE } -} // namespace hal \ No newline at end of file +} // namespace hal diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index b1ff7ffd077c..ace39cbc6c8e 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -1,3 +1,29 @@ +// 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. +// Copyright (c) 2026 Sascha Tommasone. 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. + #include "clock_tree_extractor/clock_tree.h" #include "hal_core/netlist/decorators/netlist_traversal_decorator.h" diff --git a/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp index 8e9a3e1b8783..c2030a144941 100644 --- a/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp +++ b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp @@ -1,3 +1,29 @@ +// 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. +// Copyright (c) 2026 Sascha Tommasone. 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. + #include "clock_tree_extractor/plugin_clock_tree_extractor.h" namespace hal @@ -39,4 +65,4 @@ namespace hal std::set retval; return retval; } -} // namespace hal \ No newline at end of file +} // namespace hal From 62af230e57b36eb7ec9b20b25ad4d0bb9d03c2c7 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Thu, 27 Aug 2026 23:49:59 -0400 Subject: [PATCH 21/23] update ipgraph api calls --- plugins/clock_tree_extractor/src/clock_tree.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index ace39cbc6c8e..ffd8b41f9ad6 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -526,7 +526,7 @@ namespace hal return ERR( igraph_strerror( ierror ) ); } - if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, vertex, IGRAPH_OUT ) ) != IGRAPH_SUCCESS ) + if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, vertex, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) != IGRAPH_SUCCESS ) { dot_fd.close(); igraph_vector_int_destroy( &neighbors ); @@ -574,7 +574,7 @@ namespace hal return ERR( igraph_strerror( ierror ) ); } - if( ( ierror = igraph_neighbors( m_igraph_ptr, &parents, root, IGRAPH_IN ) ) != IGRAPH_SUCCESS ) + if( ( ierror = igraph_neighbors( m_igraph_ptr, &parents, root, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) != IGRAPH_SUCCESS ) { igraph_vector_int_destroy( &parents ); return ERR( igraph_strerror( ierror ) ); @@ -812,7 +812,7 @@ namespace hal return ERR( igraph_strerror( ierror ) ); } - if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, it->second, direction ) ) != IGRAPH_SUCCESS ) + if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, it->second, direction, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) != IGRAPH_SUCCESS ) { igraph_vector_int_destroy( &neighbors ); return ERR( igraph_strerror( ierror ) ); From a993e43708b80624fdd5b7f8edd7ab89285161d9 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 28 Aug 2026 13:22:18 -0400 Subject: [PATCH 22/23] update license --- .../include/clock_tree_extractor/clock_tree.h | 2 +- .../include/clock_tree_extractor/plugin_clock_tree_extractor.h | 2 +- plugins/clock_tree_extractor/python/python_bindings.cpp | 2 +- plugins/clock_tree_extractor/src/clock_tree.cpp | 2 +- .../clock_tree_extractor/src/plugin_clock_tree_extractor.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h index cc2b44aea511..7cbe2655ef53 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/clock_tree.h @@ -4,7 +4,7 @@ // 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. -// Copyright (c) 2026 Sascha Tommasone. All rights reserved. +// Copyright (c) 2025-2026 Sascha Tommasone. 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 diff --git a/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h b/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h index de9e5a93980b..4595755eba44 100644 --- a/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h +++ b/plugins/clock_tree_extractor/include/clock_tree_extractor/plugin_clock_tree_extractor.h @@ -4,7 +4,7 @@ // 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. -// Copyright (c) 2026 Sascha Tommasone. All rights reserved. +// Copyright (c) 2025-2026 Sascha Tommasone. 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 diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index 10297805e129..a9d80e3b9b10 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -4,7 +4,7 @@ // 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. -// Copyright (c) 2026 Sascha Tommasone. All rights reserved. +// Copyright (c) 2025-2026 Sascha Tommasone. 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 diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index ffd8b41f9ad6..84eb6894767d 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -4,7 +4,7 @@ // 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. -// Copyright (c) 2026 Sascha Tommasone. All rights reserved. +// Copyright (c) 2025-2026 Sascha Tommasone. 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 diff --git a/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp index c2030a144941..e3d2457b5a7f 100644 --- a/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp +++ b/plugins/clock_tree_extractor/src/plugin_clock_tree_extractor.cpp @@ -4,7 +4,7 @@ // 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. -// Copyright (c) 2026 Sascha Tommasone. All rights reserved. +// Copyright (c) 2025-2026 Sascha Tommasone. 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 From 9abe40983ba70bbd0c638eca6b526e51d1ceb502 Mon Sep 17 00:00:00 2001 From: Sascha Tommasone Date: Fri, 28 Aug 2026 13:44:36 -0400 Subject: [PATCH 23/23] fix borrowing --- .../clock_tree_extractor/python/python_bindings.cpp | 13 +++++++++---- plugins/clock_tree_extractor/src/clock_tree.cpp | 12 +++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/plugins/clock_tree_extractor/python/python_bindings.cpp b/plugins/clock_tree_extractor/python/python_bindings.cpp index a9d80e3b9b10..a6e3ab8341e7 100644 --- a/plugins/clock_tree_extractor/python/python_bindings.cpp +++ b/plugins/clock_tree_extractor/python/python_bindings.cpp @@ -122,7 +122,7 @@ namespace hal :rtype: set[str] )" ); - py::class_>( m, "ClockTree", R"()" ) + py::class_( m, "ClockTree", R"()" ) .def_static( "from_netlist", []( const Netlist *netlist ) -> std::unique_ptr { @@ -136,6 +136,7 @@ namespace hal return nullptr; }, py::arg( "netlist" ), + py::return_value_policy::move, R"()" ) .def( "export", @@ -187,6 +188,7 @@ namespace hal } return result; }, + borrowed(), R"()" ) .def( "get_vertex_from_ptr", @@ -222,6 +224,7 @@ namespace hal return py::none(); }, py::arg( "vertex" ), + borrowed(), R"()" ) .def( "get_vertices_from_ptrs", @@ -295,6 +298,7 @@ namespace hal return py::none(); }, py::arg( "ptr" ), + borrowed(), R"()" ) .def( "get_childs", @@ -325,10 +329,11 @@ namespace hal return py::none(); }, py::arg( "ptr" ), + borrowed(), R"()" ) - .def( "get_gates", &cte::ClockTree::get_gates, R"()" ) - .def( "get_nets", &cte::ClockTree::get_nets, R"()" ) - .def( "get_netlist", &cte::ClockTree::get_netlist, R"()" ); + .def( "get_gates", &cte::ClockTree::get_gates, borrowed(), R"()" ) + .def( "get_nets", &cte::ClockTree::get_nets, borrowed(), R"()" ) + .def( "get_netlist", &cte::ClockTree::get_netlist, borrowed(), R"()" ); #ifndef PYBIND11_MODULE return m.ptr(); diff --git a/plugins/clock_tree_extractor/src/clock_tree.cpp b/plugins/clock_tree_extractor/src/clock_tree.cpp index 84eb6894767d..182846ea55d3 100644 --- a/plugins/clock_tree_extractor/src/clock_tree.cpp +++ b/plugins/clock_tree_extractor/src/clock_tree.cpp @@ -526,7 +526,9 @@ namespace hal return ERR( igraph_strerror( ierror ) ); } - if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, vertex, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) != IGRAPH_SUCCESS ) + if( ( ierror = igraph_neighbors( + m_igraph_ptr, &neighbors, vertex, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) + != IGRAPH_SUCCESS ) { dot_fd.close(); igraph_vector_int_destroy( &neighbors ); @@ -574,7 +576,9 @@ namespace hal return ERR( igraph_strerror( ierror ) ); } - if( ( ierror = igraph_neighbors( m_igraph_ptr, &parents, root, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) != IGRAPH_SUCCESS ) + if( ( ierror = igraph_neighbors( + m_igraph_ptr, &parents, root, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) + != IGRAPH_SUCCESS ) { igraph_vector_int_destroy( &parents ); return ERR( igraph_strerror( ierror ) ); @@ -812,7 +816,9 @@ namespace hal return ERR( igraph_strerror( ierror ) ); } - if( ( ierror = igraph_neighbors( m_igraph_ptr, &neighbors, it->second, direction, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) != IGRAPH_SUCCESS ) + if( ( ierror = igraph_neighbors( + m_igraph_ptr, &neighbors, it->second, direction, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE ) ) + != IGRAPH_SUCCESS ) { igraph_vector_int_destroy( &neighbors ); return ERR( igraph_strerror( ierror ) );