HAL  v4.5.0-133-g64838ea8d
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
python_bindings.cpp
Go to the documentation of this file.
1 // MIT License
2 //
3 // Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved.
4 // Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved.
5 // Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved.
6 // Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved.
7 // Copyright (c) 2025-2026 Sascha Tommasone. All rights reserved.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in all
17 // copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 // SOFTWARE.
26 
28 
31 #include "pybind11/pybind11.h"
32 
33 #include <igraph/igraph.h>
34 #include <pybind11/detail/descr.h>
35 #include <pybind11/pytypes.h>
36 #include <set>
37 #include <string>
38 #include <vector>
39 
40 namespace hal
41 {
42  class BasePluginInterface;
43 }
44 namespace hal
45 {
46  class Netlist;
47 }
48 
49 namespace py = pybind11;
50 
51 namespace hal
52 {
53 
54  // the name in PYBIND11_MODULE/PYBIND11_PLUGIN *MUST* match the filename of the output library (without extension),
55  // otherwise you will get "ImportError: dynamic module does not define module export function" when importing the
56  // module
57 
58 #ifdef PYBIND11_MODULE
59  PYBIND11_MODULE( clock_tree_extractor, m )
60  {
61  m.doc() = "";
62 #else
63  PYBIND11_PLUGIN( clock_tree_extractor )
64  {
65  py::module m( "clock_tree_extractor", "" );
66 #endif // ifdef PYBIND11_MODULE
67 
68  py::class_<ClockTreeExtractorPlugin, RawPtrWrapper<ClockTreeExtractorPlugin>, BasePluginInterface>
69  py_clock_tree_extractor_plugin( m, "ClockTreeExtractorPlugin", "" );
70 
71  py_clock_tree_extractor_plugin.def_property_readonly( "name", &ClockTreeExtractorPlugin::get_name, R"(
72  The name of the plugin.
73 
74  :type: str
75  )" );
76 
77  py_clock_tree_extractor_plugin.def( "get_name", &ClockTreeExtractorPlugin::get_name, R"(
78  Get the name of the plugin.
79 
80  :returns: The name of the plugin.
81  :rtype: str
82  )" );
83 
84  py_clock_tree_extractor_plugin.def_property_readonly( "version", &ClockTreeExtractorPlugin::get_version, R"(
85  The version of the plugin.
86 
87  :type: str
88  )" );
89 
90  py_clock_tree_extractor_plugin.def( "get_version", &ClockTreeExtractorPlugin::get_version, R"(
91  Get the version of the plugin.
92 
93  :returns: The version of the plugin.
94  :rtype: str
95  )" );
96 
97  py_clock_tree_extractor_plugin.def_property_readonly(
99  The description of the plugin.
100 
101  :type: str
102  )" );
103 
104  py_clock_tree_extractor_plugin.def( "get_description", &ClockTreeExtractorPlugin::get_description, R"(
105  Get the description of the plugin.
106 
107  :returns: The description of the plugin.
108  :rtype: str
109  )" );
110 
111  py_clock_tree_extractor_plugin.def_property_readonly(
112  "dependencies", &ClockTreeExtractorPlugin::get_dependencies, R"(
113  A set of plugin names that this plugin depends on.
114 
115  :type: set[str]
116  )" );
117 
118  py_clock_tree_extractor_plugin.def( "get_dependencies", &ClockTreeExtractorPlugin::get_dependencies, R"(
119  Get a set of plugin names that this plugin depends on.
120 
121  :returns: A set of plugin names that this plugin depends on.
122  :rtype: set[str]
123  )" );
124 
125  py::class_<cte::ClockTree>( m, "ClockTree", R"(
126  The clock distribution network of a netlist as a directed graph whose vertices are gates and nets.
127  )" )
128  .def_static(
129  "from_netlist",
130  []( const Netlist *netlist ) -> std::unique_ptr<cte::ClockTree> {
131  auto result = cte::ClockTree::from_netlist( netlist );
132  if( result.is_ok() )
133  {
134  return result.get();
135  }
136 
137  log_error( "clock_tree_extractor", "{}", result.get_error().get() );
138  return nullptr;
139  },
140  py::arg( "netlist" ),
141  py::return_value_policy::move,
142  py::keep_alive<0, 1>(),
143  R"(
144  Extract the clock tree of a netlist.
145 
146  Starting at the clock pin of every flip-flop, the extraction walks against the signal direction through buffers, inverters, delay gates, clock gates and toggle flip-flops up to the global input nets that drive them.
147 
148  :param hal_py.Netlist netlist: The netlist.
149  :returns: The clock tree on success, ``None`` otherwise.
150  :rtype: clock_tree_extractor.ClockTree or None
151  )" )
152  .def(
153  "export",
154  []( const cte::ClockTree &self, const std::string &pathname ) -> bool {
155  auto result = self.export_dot( pathname );
156  if( result.is_ok() )
157  {
158  return true;
159  }
160 
161  log_error( "clock_tree_extractor", "{}", result.get_error().get() );
162  return false;
163  },
164  py::arg( "pathname" ),
165  R"(
166  Write the clock tree to a DOT file.
167 
168  :param str pathname: The path of the file to write.
169  :returns: ``True`` on success, ``False`` otherwise.
170  :rtype: bool
171  )" )
172  .def(
173  "get_subtree",
174  []( const cte::ClockTree &self,
175  const void *ptr,
176  const bool parent ) -> std::unique_ptr<cte::ClockTree> {
177  auto result = self.get_subtree( ptr, parent );
178  if( result.is_ok() )
179  {
180  return result.get();
181  }
182 
183  log_error( "clock_tree_extractor", "{}", result.get_error().get() );
184  return nullptr;
185  },
186  py::arg( "ptr" ),
187  py::arg( "parent" ) = false,
188  py::return_value_policy::move,
189  py::keep_alive<0, 1>(),
190  R"(
191  Get the clock tree below a gate or net as a clock tree of its own.
192 
193  :param ptr: The gate or net.
194  :type ptr: hal_py.Gate or hal_py.Net
195  :param bool parent: Set ``True`` to start one level up, at the parent of the given object, if it has exactly one. Defaults to ``False``.
196  :returns: The subtree on success, ``None`` otherwise.
197  :rtype: clock_tree_extractor.ClockTree or None
198  )" )
199  .def(
200  "get_all",
201  []( const cte::ClockTree &self ) -> py::list {
202  py::list result;
203  const auto &map = self.get_all();
204  for( auto &[ptr, type] : map )
205  {
206  if( type == cte::PtrType::GATE )
207  {
208  result.append( py::cast( (const Gate *) ptr ) );
209  }
210  else if( type == cte::PtrType::NET )
211  {
212  result.append( py::cast( (const Net *) ptr ) );
213  }
214  }
215  return result;
216  },
217  borrowed(),
218  R"(
219  Get all gates and nets of the clock tree.
220 
221  :returns: A list of gates and nets.
222  :rtype: list[hal_py.Gate or hal_py.Net]
223  )" )
224  .def(
225  "get_vertex_from_ptr",
226  []( const cte::ClockTree &self, const void *ptr ) -> py::object {
227  auto result = self.get_vertex_from_ptr( ptr );
228  if( result.is_ok() )
229  {
230  return py::int_( result.get() );
231  }
232  log_error( "clock_tree_extractor", "{}", result.get_error().get() );
233  return py::none();
234  },
235  py::arg( "ptr" ),
236  R"(
237  Get the igraph vertex ID of a gate or net of the clock tree.
238 
239  :param ptr: The gate or net.
240  :type ptr: hal_py.Gate or hal_py.Net
241  :returns: The vertex ID on success, ``None`` otherwise.
242  :rtype: int or None
243  )" )
244  .def(
245  "get_ptr_from_vertex",
246  []( const cte::ClockTree &self, const igraph_integer_t vertex ) -> py::object {
247  auto result = self.get_ptr_from_vertex( vertex );
248  if( result.is_ok() )
249  {
250  auto [ptr, type] = result.get();
251  if( type == cte::PtrType::GATE )
252  {
253  return py::cast( (const Gate *) ptr );
254  }
255  else if( type == cte::PtrType::NET )
256  {
257  return py::cast( (const Net *) ptr );
258  }
259  return py::none();
260  }
261  log_error( "clock_tree_extractor", "{}", result.get_error().get() );
262  return py::none();
263  },
264  py::arg( "vertex" ),
265  borrowed(),
266  R"(
267  Get the gate or net behind an igraph vertex ID of the clock tree.
268 
269  :param int vertex: The vertex ID.
270  :returns: The gate or net on success, ``None`` otherwise.
271  :rtype: hal_py.Gate or hal_py.Net or None
272  )" )
273  .def(
274  "get_vertices_from_ptrs",
275  []( const cte::ClockTree &self, const std::vector<const void *> &ptrs ) -> py::list {
276  auto result = self.get_vertices_from_ptrs( ptrs );
277  if( result.is_ok() )
278  {
279  return py::cast( result.get() );
280  }
281  log_error( "clock_tree_extractor", "{}", result.get_error().get() );
282  return py::none();
283  },
284  py::arg( "ptrs" ),
285  R"(
286  Get the igraph vertex IDs of gates and nets of the clock tree.
287 
288  :param list[hal_py.Gate or hal_py.Net] ptrs: The gates and nets.
289  :returns: The vertex IDs on success, ``None`` otherwise.
290  :rtype: list[int] or None
291  )" )
292  .def(
293  "get_ptrs_from_vertices",
294  []( const cte::ClockTree &self, const std::vector<igraph_integer_t> &vertices ) -> py::list {
295  auto res = self.get_ptrs_from_vertices( vertices );
296  if( res.is_ok() )
297  {
298  py::list result;
299  for( const auto &[ptr, type] : res.get() )
300  {
301  if( type == cte::PtrType::GATE )
302  {
303  result.append( py::cast( (const Gate *) ptr ) );
304  }
305  else if( type == cte::PtrType::NET )
306  {
307  result.append( py::cast( (const Net *) ptr ) );
308  }
309  else
310  {
311  log_error( "clock_tree_extractor", "unknown ptr type" );
312  return py::none();
313  }
314  }
315  return result;
316  }
317  log_error( "clock_tree_extractor", "{}", res.get_error().get() );
318  return py::none();
319  },
320  py::arg( "vertices" ),
321  borrowed(),
322  R"(
323  Get the gates and nets behind igraph vertex IDs of the clock tree.
324 
325  :param list[int] vertices: The vertex IDs.
326  :returns: The gates and nets on success, ``None`` otherwise.
327  :rtype: list[hal_py.Gate or hal_py.Net] or None
328  )" )
329  .def(
330  "get_parents",
331  []( const cte::ClockTree &self, const void *ptr ) -> py::list {
332  auto res = self.get_neighbors( ptr, IGRAPH_IN );
333  if( res.is_ok() )
334  {
335  py::list result;
336  for( const auto &[ptr, type] : res.get() )
337  {
338  if( type == cte::PtrType::GATE )
339  {
340  result.append( py::cast( (const Gate *) ptr ) );
341  }
342  else if( type == cte::PtrType::NET )
343  {
344  result.append( py::cast( (const Net *) ptr ) );
345  }
346  else
347  {
348  log_error( "clock_tree_extractor", "unknown ptr type" );
349  return py::none();
350  }
351  }
352  return result;
353  }
354  log_error( "clock_tree_extractor", "{}", res.get_error().get() );
355  return py::none();
356  },
357  py::arg( "ptr" ),
358  borrowed(),
359  R"(
360  Get the gates and nets directly upstream of a gate or net in the clock tree.
361 
362  :param ptr: The gate or net.
363  :type ptr: hal_py.Gate or hal_py.Net
364  :returns: The neighbors on success, ``None`` otherwise.
365  :rtype: list[hal_py.Gate or hal_py.Net] or None
366  )" )
367  .def(
368  "get_childs",
369  []( const cte::ClockTree &self, const void *ptr ) -> py::list {
370  auto res = self.get_neighbors( ptr, IGRAPH_OUT );
371  if( res.is_ok() )
372  {
373  py::list result;
374  for( const auto &[ptr, type] : res.get() )
375  {
376  if( type == cte::PtrType::GATE )
377  {
378  result.append( py::cast( (const Gate *) ptr ) );
379  }
380  else if( type == cte::PtrType::NET )
381  {
382  result.append( py::cast( (const Net *) ptr ) );
383  }
384  else
385  {
386  log_error( "clock_tree_extractor", "unknown ptr type" );
387  return py::none();
388  }
389  }
390  return result;
391  }
392  log_error( "clock_tree_extractor", "{}", res.get_error().get() );
393  return py::none();
394  },
395  py::arg( "ptr" ),
396  borrowed(),
397  R"(
398  Get the gates and nets directly downstream of a gate or net in the clock tree.
399 
400  :param ptr: The gate or net.
401  :type ptr: hal_py.Gate or hal_py.Net
402  :returns: The neighbors on success, ``None`` otherwise.
403  :rtype: list[hal_py.Gate or hal_py.Net] or None
404  )" )
405  .def( "get_gates", &cte::ClockTree::get_gates, borrowed(), R"(
406  Get all gates of the clock tree.
407 
408  :returns: The gates.
409  :rtype: list[hal_py.Gate]
410  )" )
411  .def( "get_nets", &cte::ClockTree::get_nets, borrowed(), R"(
412  Get all nets of the clock tree.
413 
414  :returns: The nets.
415  :rtype: list[hal_py.Net]
416  )" )
417  .def( "get_netlist", &cte::ClockTree::get_netlist, borrowed(), R"(
418  Get the netlist the clock tree was extracted from.
419 
420  :returns: The netlist.
421  :rtype: hal_py.Netlist
422  )" );
423 
424 #ifndef PYBIND11_MODULE
425  return m.ptr();
426 #endif // PYBIND11_MODULE
427  }
428 } // namespace hal
std::string get_description() const override
std::string get_name() const override
std::set< std::string > get_dependencies() const override
std::string get_version() const override
Definition: gate.h:58
Definition: net.h:58
const Netlist * get_netlist() const
Definition: clock_tree.cpp:794
const std::vector< const Gate * > get_gates() const
Definition: clock_tree.cpp:759
const std::vector< const Net * > get_nets() const
Definition: clock_tree.cpp:774
static Result< std::unique_ptr< ClockTree > > from_netlist(const Netlist *netlist)
Definition: clock_tree.cpp:160
#define log_error(channel,...)
Definition: log.h:78
const Module * module(const Gate *g, const NodeBoxes &boxes)
Definition: defines.h:45
PYBIND11_PLUGIN(hal_py)
PinType type