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.
2 
5 #include "pybind11/operators.h"
6 #include "pybind11/pybind11.h"
7 #include "pybind11/stl.h"
8 #include "pybind11/stl_bind.h"
9 
10 namespace py = pybind11;
11 
12 namespace hal
13 {
14 
15  // the name in PYBIND11_MODULE/PYBIND11_PLUGIN *MUST* match the filename of the output library (without extension),
16  // otherwise you will get "ImportError: dynamic module does not define module export function" when importing the module
17 
18 #ifdef PYBIND11_MODULE
19  PYBIND11_MODULE(netlist_preprocessing, m)
20  {
21  m.doc() = "hal NetlistPreprocessingPlugin python bindings";
22 #else
23  PYBIND11_PLUGIN(netlist_preprocessing)
24  {
25  py::module m("netlist_preprocessing", "hal NetlistPreprocessingPlugin python bindings");
26 #endif // ifdef PYBIND11_MODULE
27 
28  py::class_<NetlistPreprocessingPlugin, RawPtrWrapper<NetlistPreprocessingPlugin>, BasePluginInterface> py_netlist_preprocessing(m, "NetlistPreprocessingPlugin");
29  py_netlist_preprocessing.def_property_readonly("name", &NetlistPreprocessingPlugin::get_name, R"(
30  The name of the plugin.
31 
32  :type: str
33  )");
34 
35  py_netlist_preprocessing.def("get_name", &NetlistPreprocessingPlugin::get_name, R"(
36  Get the name of the plugin.
37 
38  :returns: Plugin name.
39  :rtype: str
40  )");
41 
42  py_netlist_preprocessing.def_property_readonly("version", &NetlistPreprocessingPlugin::get_version, R"(
43  The version of the plugin.
44 
45  :type: str
46  )");
47 
48  py_netlist_preprocessing.def("get_version", &NetlistPreprocessingPlugin::get_version, R"(
49  Get the version of the plugin.
50 
51  :returns: Plugin version.
52  :rtype: str
53  )");
54 
55  py_netlist_preprocessing.def_property_readonly("description", &NetlistPreprocessingPlugin::get_description, R"(
56  The description of the plugin.
57 
58  :type: str
59  )");
60 
61  py_netlist_preprocessing.def("get_description", &NetlistPreprocessingPlugin::get_description, R"(
62  Get the description of the plugin.
63 
64  :returns: The description of the plugin.
65  :rtype: str
66  )");
67 
68  py_netlist_preprocessing.def_property_readonly("dependencies", &NetlistPreprocessingPlugin::get_dependencies, R"(
69  A set of plugin names that this plugin depends on.
70 
71  :type: set[str]
72  )");
73 
74  py_netlist_preprocessing.def("get_dependencies", &NetlistPreprocessingPlugin::get_dependencies, R"(
75  Get a set of plugin names that this plugin depends on.
76 
77  :returns: A set of plugin names that this plugin depends on.
78  :rtype: set[str]
79  )");
80 
81  m.def(
82  "remove_unused_lut_inputs",
83  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::optional<u32> {
85  if (res.is_ok())
86  {
87  return res.get();
88  }
89  else
90  {
91  log_error("python_context", "{}", res.get_error().get());
92  return std::nullopt;
93  }
94  },
95  py::arg("nl"),
96  py::arg("gates") = std::vector<Gate*>(),
97  R"(
98  Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate.
99 
100  :param hal_py.Netlist nl: The netlist to operate on.
101  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
102  :returns: The number of removed LUT endpoints on success, ``None`` otherwise.
103  :rtype: int or ``None``
104  )");
105 
106  m.def(
107  "remove_buffers",
108  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::optional<u32> {
109  auto res = netlist_preprocessing::remove_buffers(nl, gates);
110  if (res.is_ok())
111  {
112  return res.get();
113  }
114  else
115  {
116  log_error("python_context", "{}", res.get_error().get());
117  return std::nullopt;
118  }
119  },
120  py::arg("nl"),
121  py::arg("gates") = std::vector<Gate*>(),
122  R"(
123  Removes buffer gates from the netlist and connect their fan-in to their fan-out nets.
124  Considers all combinational gates and takes their inputs into account.
125  For example, a 2-input AND gate with one input being connected to constant ``1`` will also be removed.
126 
127  :param hal_py.Netlist nl: The netlist to operate on.
128  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
129  :returns: The number of removed buffers on success, ``None`` otherwise.
130  :rtype: int or ``None``
131  )");
132 
133  m.def(
134  "remove_redundant_gates",
135  [](Netlist* nl, const std::function<bool(const Gate*)>& filter = nullptr, const std::vector<Gate*>& gates = {}) -> std::optional<u32> {
136  auto res = netlist_preprocessing::remove_redundant_gates(nl, filter, gates);
137  if (res.is_ok())
138  {
139  return res.get();
140  }
141  else
142  {
143  log_error("python_context", "{}", res.get_error().get());
144  return std::nullopt;
145  }
146  },
147  py::arg("nl"),
148  py::arg("filter") = nullptr,
149  py::arg("gates") = std::vector<Gate*>(),
150  R"(
151  Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets.
152  Only gates contained in ``gates`` are removed, the equivalent gate that is kept in their stead may lie outside of ``gates``.
153 
154  :param hal_py.Netlist nl: The netlist to operate on.
155  :param lambda filter: Optional filter to fine-tune which gates are being replaced. Default to a ``None``.
156  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
157  :returns: The number of removed gates on success, ``None`` otherwise.
158  :rtype: int or ``None``
159  )");
160 
161  m.def(
162  "remove_redundant_loops",
163  [](Netlist* nl) -> std::optional<u32> {
165  if (res.is_ok())
166  {
167  return res.get();
168  }
169  else
170  {
171  log_error("python_context", "{}", res.get_error().get());
172  return std::nullopt;
173  }
174  },
175  py::arg("nl"),
176  R"(
177  Removes redundant sequential feedback loops.
178  Sometimes flip-flops and some of their combinational fan-in form a feedback loop where the flip-flop input depends on its own output.
179  For optimization, some synthesizers create multiple equivalent instances of these feedback loops.
180  To simplify structural analysis, this function removes the redundant flip-flop gate of the loop from the netlist.
181  Other preprocessing functions can then take care of the remaining combination gates of the loop.
182 
183  :param hal_py.Netlist nl: The netlist to operate on.
184  :returns: The number of removed gates on success, ``None`` otherwise.
185  :rtype: int or ``None``
186  )");
187 
188  m.def(
189  "remove_redundant_logic_trees",
190  [](Netlist* nl) -> std::optional<u32> {
192  if (res.is_ok())
193  {
194  return res.get();
195  }
196  else
197  {
198  log_error("python_context", "{}", res.get_error().get());
199  return std::nullopt;
200  }
201  },
202  py::arg("nl"),
203  R"(
204  Removes redundant logic trees made up of combinational gates.
205  If two trees compute the exact same function even if implemented with different gates we will disconnect one of the trees and afterwards clean up all dangling gates and nets.
206 
207  :param hal_py.Netlist nl: The netlist to operate on.
208  :returns: The number of removed gates on success, ``None`` otherwise.
209  :rtype: int or ``None``
210  )");
211 
212  m.def(
213  "remove_unconnected_gates",
214  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::optional<u32> {
216  if (res.is_ok())
217  {
218  return res.get();
219  }
220  else
221  {
222  log_error("python_context", "{}", res.get_error().get());
223  return std::nullopt;
224  }
225  },
226  py::arg("nl"),
227  py::arg("gates") = std::vector<Gate*>(),
228  R"(
229  Removes gates for which all fan-out nets do not have a destination and are not global output nets.
230  The removal is repeated until no further gates can be removed, but gates outside of ``gates`` are never removed, even if they become unconnected in the process.
231 
232  :param hal_py.Netlist nl: The netlist to operate on.
233  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
234  :returns: The number of removed gates on success, ``None`` otherwise.
235  :rtype: int or ``None``
236  )");
237 
238  m.def(
239  "remove_unconnected_nets",
240  [](Netlist* nl) -> std::optional<u32> {
242  if (res.is_ok())
243  {
244  return res.get();
245  }
246  else
247  {
248  log_error("python_context", "{}", res.get_error().get());
249  return std::nullopt;
250  }
251  },
252  py::arg("nl"),
253  R"(
254  Removes nets who have neither a source, nor a destination.
255 
256  :param hal_py.Netlist nl: The netlist to operate on.
257  :returns: The number of removed nets on success, ``None`` otherwise.
258  :rtype: int or ``None``
259  )");
260 
261  m.def(
262  "remove_unconnected_looped",
263  [](Netlist* nl) -> std::optional<u32> {
265  if (res.is_ok())
266  {
267  return res.get();
268  }
269  else
270  {
271  log_error("python_context", "{}", res.get_error().get());
272  return std::nullopt;
273  }
274  },
275  py::arg("nl"),
276  R"(
277  Calls remove_unconnected_gates / remove_unconnected_nets until there are no further changes.
278 
279  :param hal_py.Netlist nl: The netlist to operate on.
280  :returns: The number of removed nets and gates on success, ``None`` otherwise.
281  :rtype: int or ``None``
282  )");
283 
284  m.def(
285  "manual_mux_optimizations",
286  [](Netlist* nl, GateLibrary* mux_inv_gl) -> std::optional<u32> {
287  auto res = netlist_preprocessing::manual_mux_optimizations(nl, mux_inv_gl);
288  if (res.is_ok())
289  {
290  return res.get();
291  }
292  else
293  {
294  log_error("python_context", "{}", res.get_error().get());
295  return std::nullopt;
296  }
297  },
298  py::arg("nl"),
299  py::arg("mux_inv_gl"),
300  R"(
301  Apply manually implemented optimizations to the netlist centered around muxes.
302  Currently implemented optimizations include:
303 
304  - removing inverters in case there are inverter gates in front of and behind every data input and output of the mux
305  - optimizing and therefore unifying possible inverters preceding the select signals by resynthesizing
306 
307  :param hal_py.Netlist nl: The netlist to operate on.
308  :param hal_py.GateLibrary mux_inv_gl: A gate library only containing mux and inverter gates used for resynthesis.
309  :returns: The difference in the total number of gates caused by these optimizations.
310  :rtype: int or ``None``
311  )");
312 
313  m.def(
314  "propagate_constants",
315  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::optional<u32> {
316  auto res = netlist_preprocessing::propagate_constants(nl, gates);
317  if (res.is_ok())
318  {
319  return res.get();
320  }
321  else
322  {
323  log_error("python_context", "{}", res.get_error().get());
324  return std::nullopt;
325  }
326  },
327  py::arg("nl"),
328  py::arg("gates") = std::vector<Gate*>(),
329  R"(
330  Builds for all gate output nets the Boolean function and substitutes all variables connected to vcc/gnd nets with the respective boolean value.
331  If the function simplifies to a static boolean constant cut the connection to the nets destinations and directly connect it to vcc/gnd.
332  The propagation is repeated until no further gates can be substituted, but gates outside of ``gates`` are never substituted, even if they become constant in the process.
333 
334  :param hal_py.Netlist nl: The netlist to operate on.
335  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
336  :returns: The number of rerouted nets on success, ``None`` otherwise.
337  :rtype: int or ``None``
338  )");
339 
340  m.def(
341  "remove_consecutive_inverters",
342  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::optional<u32> {
344  if (res.is_ok())
345  {
346  return res.get();
347  }
348  else
349  {
350  log_error("python_context", "{}", res.get_error().get());
351  return std::nullopt;
352  }
353  },
354  py::arg("nl"),
355  py::arg("gates") = std::vector<Gate*>(),
356  R"(
357  Removes two consecutive inverters and reconnects the input of the first inverter to the output of the second one.
358  If the first inverter has additional successors, only the second inverter is deleted.
359  Both inverters must be contained in ``gates`` for the pair to be considered, even if only the second one ends up being deleted.
360 
361  :param hal_py.Netlist nl: The netlist to operate on.
362  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
363  :returns: The number of removed inverter gates on success, ``None`` otherwise.
364  :rtype: int or ``None``
365  )");
366 
367  m.def(
368  "simplify_lut_inits",
369  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::optional<u32> {
370  auto res = netlist_preprocessing::simplify_lut_inits(nl, gates);
371  if (res.is_ok())
372  {
373  return res.get();
374  }
375  else
376  {
377  log_error("python_context", "{}", res.get_error().get());
378  return std::nullopt;
379  }
380  },
381  py::arg("nl"),
382  py::arg("gates") = std::vector<Gate*>(),
383  R"(
384  Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string.
385 
386  :param hal_py.Netlist nl: The netlist to operate on.
387  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
388  :returns: The number of simplified INIT strings on success, ``None`` otherwise.
389  :rtype: int or ``None``
390  )");
391 
392  m.def(
393  "reconstruct_indexed_ff_identifiers",
394  [](Netlist* nl) -> std::optional<u32> {
396  if (res.is_ok())
397  {
398  return res.get();
399  }
400  else
401  {
402  log_error("python_context", "{}", res.get_error().get());
403  return std::nullopt;
404  }
405  },
406  py::arg("nl"),
407  R"(
408  Tries to reconstruct a name and index for each flip flop that was part of a multi-bit wire in the verilog code.
409  This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer.
410  This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self.
411  We try to reconstruct name and index for each flip flop based on the name of its output nets.
412 
413  :param hal_py.Netlist nl: The netlist to operate on.
414  :returns: The number of reconstructed names on success, ``None`` otherwise.
415  :rtype: int or ``None``
416  )");
417 
418  m.def(
419  "reconstruct_top_module_pin_groups",
420  [](Netlist* nl) -> std::optional<u32> {
422  if (res.is_ok())
423  {
424  return res.get();
425  }
426  else
427  {
428  log_error("python_context", "{}", res.get_error().get());
429  return std::nullopt;
430  }
431  },
432  py::arg("nl"),
433  R"(
434  Tries to reconstruct top module pin groups via indexed pin names.
435 
436  :param hal_py.Netlist nl: The netlist to operate on.
437  :returns: The number of reconstructed pin groups on success, ``None`` otherwise.
438  :rtype: int or ``None``
439  )");
440 
441  m.def(
442  "parse_def_file",
443  [](Netlist* nl, const std::filesystem::path& def_file) -> bool {
444  auto res = netlist_preprocessing::parse_def_file(nl, def_file);
445  if (res.is_ok())
446  {
447  return true;
448  }
449  else
450  {
451  log_error("python_context", "{}", res.get_error().get());
452  return false;
453  }
454  },
455  py::arg("nl"),
456  py::arg("def_file"),
457  R"(
458  Parses a design exchange format file and extracts the coordinates of a placed design for each component/gate.
459  The extracted coordinates get annotated to the gates.
460 
461  :param hal_py.Netlist nl: The netlist to operate on.
462  :param pathlib.Path def_file: The path to the def file
463  :returns: ``True`` on success, ``False`` otherwise.
464  :rtype: bool
465  )");
466 
467  m.def(
468  "create_multi_bit_gate_modules",
469  [](Netlist* nl, const std::map<std::string, std::map<std::string, std::vector<std::string>>>& concatenated_pin_groups) -> std::vector<Module*> {
470  auto res = netlist_preprocessing::create_multi_bit_gate_modules(nl, concatenated_pin_groups);
471  if (res.is_ok())
472  {
473  return res.get();
474  }
475  else
476  {
477  log_error("python_context", "{}", res.get_error().get());
478  return {};
479  }
480  },
481  py::arg("nl"),
482  py::arg("concatenated_pin_groups"),
483  borrowed(),
484  R"(
485  Create modules from large gates like RAMs and DSPs with the option to concatenate multiple gate pin groups into larger consecutive pin groups.
486 
487  :param hal_py.Netlist nl: The netlist to operate on.
488  :param dict[str,dict[str,list[str]]] concatenated_pin_groups: A dict from gate type name to a dict from the name of the resulting pin group to the names of the pin groups it is concatenated from.
489  :returns: The created modules on success, an empty list otherwise.
490  :rtype: list[hal_py.Module]
491  )");
492 
493  m.def(
494  "create_nets_at_unconnected_pins",
495  [](Netlist* nl, const std::vector<Gate*>& gates) -> std::vector<Net*> {
497  if (res.is_ok())
498  {
499  return res.get();
500  }
501  else
502  {
503  log_error("python_context", "{}", res.get_error().get());
504  return {};
505  }
506  },
507  py::arg("nl"),
508  py::arg("gates") = std::vector<Gate*>(),
509  borrowed(),
510  R"(
511  Create a new net for every unconnected output pin of every gate of the netlist.
512  The new nets are named ``HAL_UNCONNECTED_<net_id>``.
513 
514  :param hal_py.Netlist nl: The netlist to operate on.
515  :param list[hal_py.Gate] gates: The gates to consider. Defaults to an empty list, in which case all gates of the netlist are considered.
516  :returns: The created nets on success, an empty list otherwise.
517  :rtype: list[hal_py.Net]
518  )");
519 
520  m.def(
521  "unify_ff_outputs",
522  [](Netlist* nl, const std::vector<Gate*>& ffs = {}, GateType* inverter_type = nullptr) -> std::optional<u32> {
523  auto res = netlist_preprocessing::unify_ff_outputs(nl, ffs, inverter_type);
524  if (res.is_ok())
525  {
526  return res.get();
527  }
528  else
529  {
530  log_error("python_context", "{}", res.get_error().get());
531  return std::nullopt;
532  }
533  },
534  py::arg("nl"),
535  py::arg("ffs") = std::vector<Gate*>(),
536  py::arg("inverter_type") = nullptr,
537  R"(
538  Iterates all flip-flops of the netlist or specified by the user.
539  If a flip-flop has a ``state`` and a ``neg_state`` output, a new inverter gate is created and connected to the ``state`` output net as an additional destination.
540  Finally, the ``neg_state`` output net is disconnected from the ``neg_state`` pin and re-connected to the new inverter gate's output.
541  The new inverter gate is assigned to the module of the respective flip-flop.
542 
543  :param hal_py.Netlist nl: The netlist to operate on.
544  :param list[hal_py.Gate] ffs: The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered.
545  :param hal_py.GateType inverter_type: The inverter gate type to use. Defaults to a ``None``, in which case the first inverter type found in the gate library is used.
546  :returns: The number of rerouted ``neg_state`` outputs on success, ``None`` otherwise.
547  :rtype: int or ``None``
548  )");
549 
550 #ifndef PYBIND11_MODULE
551  return m.ptr();
552 #endif // PYBIND11_MODULE
553  }
554 } // namespace hal
Definition: gate.h:58
std::string get_name() const override
Get the name of the plugin.
std::string get_description() const override
Get a short description of the plugin.
std::string get_version() const override
Get the version of the plugin.
std::set< std::string > get_dependencies() const override
Get the plugin dependencies.
#define log_error(channel,...)
Definition: log.h:78
const Module * module(const Gate *g, const NodeBoxes &boxes)
Result< std::monostate > parse_def_file(Netlist *nl, const std::filesystem::path &def_file)
Result< u32 > manual_mux_optimizations(Netlist *nl, GateLibrary *mux_inv_gl)
Result< u32 > propagate_constants(Netlist *nl, const std::vector< Gate * > &gates={})
Result< std::vector< Net * > > create_nets_at_unconnected_pins(Netlist *nl, const std::vector< Gate * > &gates={})
Result< u32 > remove_unconnected_gates(Netlist *nl, const std::vector< Gate * > &gates={})
Result< u32 > remove_redundant_loops(Netlist *nl)
Result< u32 > remove_redundant_logic_trees(Netlist *nl)
Result< u32 > remove_redundant_gates(Netlist *nl, const std::function< bool(const Gate *)> &filter=nullptr, const std::vector< Gate * > &gates={})
Result< u32 > reconstruct_top_module_pin_groups(Netlist *nl)
Result< u32 > simplify_lut_inits(Netlist *nl, const std::vector< Gate * > &gates={})
Result< u32 > remove_buffers(Netlist *nl, const std::vector< Gate * > &gates={})
Result< u32 > remove_unused_lut_inputs(Netlist *nl, const std::vector< Gate * > &gates={})
Result< u32 > remove_unconnected_looped(Netlist *nl)
Result< u32 > remove_unconnected_nets(Netlist *nl)
Result< u32 > reconstruct_indexed_ff_identifiers(Netlist *nl)
Result< u32 > remove_consecutive_inverters(Netlist *nl, const std::vector< Gate * > &gates={})
Result< std::vector< Module * > > create_multi_bit_gate_modules(Netlist *nl, const std::map< std::string, std::map< std::string, std::vector< std::string >>> &concatenated_pin_groups)
Result< u32 > unify_ff_outputs(Netlist *nl, const std::vector< Gate * > &ffs={}, GateType *inverter_type=nullptr)
Definition: defines.h:45
PYBIND11_PLUGIN(hal_py)
This file contains all functions related to the HAL plugin API.