HAL  v4.5.0-83-g30c8f0afc
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) -> 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  R"(
97  Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate.
98 
99  :param hal_py.Netlist nl: The netlist to operate on.
100  :returns: The number of removed LUT endpoints on success, ``None`` otherwise.
101  :rtype: int or ``None``
102  )");
103 
104  m.def(
105  "remove_buffers",
106  [](Netlist* nl) -> std::optional<u32> {
108  if (res.is_ok())
109  {
110  return res.get();
111  }
112  else
113  {
114  log_error("python_context", "{}", res.get_error().get());
115  return std::nullopt;
116  }
117  },
118  py::arg("nl"),
119  R"(
120  Removes buffer gates from the netlist and connect their fan-in to their fan-out nets.
121  Considers all combinational gates and takes their inputs into account.
122  For example, a 2-input AND gate with one input being connected to constant ``1`` will also be removed.
123 
124  :param hal_py.Netlist nl: The netlist to operate on.
125  :returns: The number of removed buffers on success, ``None`` otherwise.
126  :rtype: int or ``None``
127  )");
128 
129  m.def(
130  "remove_redundant_gates",
131  [](Netlist* nl, const std::function<bool(const Gate*)>& filter = nullptr) -> std::optional<u32> {
132  auto res = netlist_preprocessing::remove_redundant_gates(nl, filter);
133  if (res.is_ok())
134  {
135  return res.get();
136  }
137  else
138  {
139  log_error("python_context", "{}", res.get_error().get());
140  return std::nullopt;
141  }
142  },
143  py::arg("nl"),
144  py::arg("filter") = nullptr,
145  R"(
146  Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets.
147 
148  :param hal_py.Netlist nl: The netlist to operate on.
149  :param lambda filter: Optional filter to fine-tune which gates are being replaced. Default to a ``None``.
150  :returns: The number of removed gates on success, ``None`` otherwise.
151  :rtype: int or ``None``
152  )");
153 
154  m.def(
155  "remove_redundant_loops",
156  [](Netlist* nl) -> std::optional<u32> {
158  if (res.is_ok())
159  {
160  return res.get();
161  }
162  else
163  {
164  log_error("python_context", "{}", res.get_error().get());
165  return std::nullopt;
166  }
167  },
168  py::arg("nl"),
169  R"(
170  Removes redundant sequential feedback loops.
171  Sometimes flip-flops and some of their combinational fan-in form a feedback loop where the flip-flop input depends on its own output.
172  For optimization, some synthesizers create multiple equivalent instances of these feedback loops.
173  To simplify structural analysis, this function removes the redundant flip-flop gate of the loop from the netlist.
174  Other preprocessing functions can then take care of the remaining combination gates of the loop.
175 
176  :param hal_py.Netlist nl: The netlist to operate on.
177  :returns: The number of removed gates on success, ``None`` otherwise.
178  :rtype: int or ``None``
179  )");
180 
181  m.def(
182  "remove_redundant_logic_trees",
183  [](Netlist* nl) -> std::optional<u32> {
185  if (res.is_ok())
186  {
187  return res.get();
188  }
189  else
190  {
191  log_error("python_context", "{}", res.get_error().get());
192  return std::nullopt;
193  }
194  },
195  py::arg("nl"),
196  R"(
197  Removes redundant logic trees made up of combinational gates.
198  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.
199 
200  :param hal_py.Netlist nl: The netlist to operate on.
201  :returns: The number of removed gates on success, ``None`` otherwise.
202  :rtype: int or ``None``
203  )");
204 
205  m.def(
206  "remove_unconnected_gates",
207  [](Netlist* nl) -> std::optional<u32> {
209  if (res.is_ok())
210  {
211  return res.get();
212  }
213  else
214  {
215  log_error("python_context", "{}", res.get_error().get());
216  return std::nullopt;
217  }
218  },
219  py::arg("nl"),
220  R"(
221  Removes gates for which all fan-out nets do not have a destination and are not global output nets.
222 
223  :param hal_py.Netlist nl: The netlist to operate on.
224  :returns: The number of removed gates on success, ``None`` otherwise.
225  :rtype: int or ``None``
226  )");
227 
228  m.def(
229  "remove_unconnected_nets",
230  [](Netlist* nl) -> std::optional<u32> {
232  if (res.is_ok())
233  {
234  return res.get();
235  }
236  else
237  {
238  log_error("python_context", "{}", res.get_error().get());
239  return std::nullopt;
240  }
241  },
242  py::arg("nl"),
243  R"(
244  Removes nets who have neither a source, nor a destination.
245 
246  :param hal_py.Netlist nl: The netlist to operate on.
247  :returns: The number of removed nets on success, ``None`` otherwise.
248  :rtype: int or ``None``
249  )");
250 
251  m.def(
252  "remove_unconnected_looped",
253  [](Netlist* nl) -> std::optional<u32> {
255  if (res.is_ok())
256  {
257  return res.get();
258  }
259  else
260  {
261  log_error("python_context", "{}", res.get_error().get());
262  return std::nullopt;
263  }
264  },
265  py::arg("nl"),
266  R"(
267  Calls remove_unconnected_gates / remove_unconnected_nets until there are no further changes.
268 
269  :param hal_py.Netlist nl: The netlist to operate on.
270  :returns: The number of removed nets and gates on success, ``None`` otherwise.
271  :rtype: int or ``None``
272  )");
273 
274  m.def(
275  "manual_mux_optimizations",
276  [](Netlist* nl, GateLibrary* mux_inv_gl) -> std::optional<u32> {
277  auto res = netlist_preprocessing::manual_mux_optimizations(nl, mux_inv_gl);
278  if (res.is_ok())
279  {
280  return res.get();
281  }
282  else
283  {
284  log_error("python_context", "{}", res.get_error().get());
285  return std::nullopt;
286  }
287  },
288  py::arg("nl"),
289  py::arg("mux_inv_gl"),
290  R"(
291  Apply manually implemented optimizations to the netlist centered around muxes.
292  Currently implemented optimizations include:
293 
294  - removing inverters in case there are inverter gates in front of and behind every data input and output of the mux
295  - optimizing and therefore unifying possible inverters preceding the select signals by resynthesizing
296 
297  :param hal_py.Netlist nl: The netlist to operate on.
298  :param hal_py.GateLibrary mux_inv_gl: A gate library only containing mux and inverter gates used for resynthesis.
299  :returns: The difference in the total number of gates caused by these optimizations.
300  :rtype: int or ``None``
301  )");
302 
303  m.def(
304  "propagate_constants",
305  [](Netlist* nl) -> std::optional<u32> {
307  if (res.is_ok())
308  {
309  return res.get();
310  }
311  else
312  {
313  log_error("python_context", "{}", res.get_error().get());
314  return std::nullopt;
315  }
316  },
317  py::arg("nl"),
318  R"(
319  Builds for all gate output nets the Boolean function and substitutes all variables connected to vcc/gnd nets with the respective boolean value.
320  If the function simplifies to a static boolean constant cut the connection to the nets destinations and directly connect it to vcc/gnd.
321 
322  :param hal_py.Netlist nl: The netlist to operate on.
323  :returns: The number of rerouted nets on success, ``None`` otherwise.
324  :rtype: int or ``None``
325  )");
326 
327  m.def(
328  "remove_consecutive_inverters",
329  [](Netlist* nl) -> std::optional<u32> {
331  if (res.is_ok())
332  {
333  return res.get();
334  }
335  else
336  {
337  log_error("python_context", "{}", res.get_error().get());
338  return std::nullopt;
339  }
340  },
341  py::arg("nl"),
342  R"(
343  Removes two consecutive inverters and reconnects the input of the first inverter to the output of the second one.
344  If the first inverter has additional successors, only the second inverter is deleted.
345 
346  :param hal_py.Netlist nl: The netlist to operate on.
347  :returns: The number of removed inverter gates on success, ``None`` otherwise.
348  :rtype: int or ``None``
349  )");
350 
351  m.def(
352  "simplify_lut_inits",
353  [](Netlist* nl) -> std::optional<u32> {
355  if (res.is_ok())
356  {
357  return res.get();
358  }
359  else
360  {
361  log_error("python_context", "{}", res.get_error().get());
362  return std::nullopt;
363  }
364  },
365  py::arg("nl"),
366  R"(
367  Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string.
368 
369  :param hal_py.Netlist nl: The netlist to operate on.
370  :returns: The number of simplified INIT strings on success, ``None`` otherwise.
371  :rtype: int or ``None``
372  )");
373 
374  m.def(
375  "reconstruct_indexed_ff_identifiers",
376  [](Netlist* nl) -> std::optional<u32> {
378  if (res.is_ok())
379  {
380  return res.get();
381  }
382  else
383  {
384  log_error("python_context", "{}", res.get_error().get());
385  return std::nullopt;
386  }
387  },
388  py::arg("nl"),
389  R"(
390  Tries to reconstruct a name and index for each flip flop that was part of a multi-bit wire in the verilog code.
391  This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer.
392  This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self.
393  We try to reconstruct name and index for each flip flop based on the name of its output nets.
394 
395  :param hal_py.Netlist nl: The netlist to operate on.
396  :returns: The number of reconstructed names on success, ``None`` otherwise.
397  :rtype: int or ``None``
398  )");
399 
400  m.def(
401  "reconstruct_top_module_pin_groups",
402  [](Netlist* nl) -> std::optional<u32> {
404  if (res.is_ok())
405  {
406  return res.get();
407  }
408  else
409  {
410  log_error("python_context", "{}", res.get_error().get());
411  return std::nullopt;
412  }
413  },
414  py::arg("nl"),
415  R"(
416  Tries to reconstruct top module pin groups via indexed pin names.
417 
418  :param hal_py.Netlist nl: The netlist to operate on.
419  :returns: The number of reconstructed pin groups on success, ``None`` otherwise.
420  :rtype: int or ``None``
421  )");
422 
423  m.def(
424  "parse_def_file",
425  [](Netlist* nl, const std::filesystem::path& def_file) -> bool {
426  auto res = netlist_preprocessing::parse_def_file(nl, def_file);
427  if (res.is_ok())
428  {
429  return true;
430  }
431  else
432  {
433  log_error("python_context", "{}", res.get_error().get());
434  return false;
435  }
436  },
437  py::arg("nl"),
438  py::arg("def_file"),
439  R"(
440  Parses a design exchange format file and extracts the coordinates of a placed design for each component/gate.
441  The extracted coordinates get annotated to the gates.
442 
443  :param hal_py.Netlist nl: The netlist to operate on.
444  :param pathlib.Path def_file: The path to the def file
445  :returns: ``True`` on success, ``False`` otherwise.
446  :rtype: bool
447  )");
448 
449  m.def(
450  "create_multi_bit_gate_modules",
451  [](Netlist* nl, const std::map<std::string, std::map<std::string, std::vector<std::string>>>& concatenated_pin_groups) -> std::vector<Module*> {
452  auto res = netlist_preprocessing::create_multi_bit_gate_modules(nl, concatenated_pin_groups);
453  if (res.is_ok())
454  {
455  return res.get();
456  }
457  else
458  {
459  log_error("python_context", "{}", res.get_error().get());
460  return {};
461  }
462  },
463  py::arg("nl"),
464  py::arg("concatenated_pin_groups"),
465  R"(
466  Create modules from large gates like RAMs and DSPs with the option to concatenate multiple gate pin groups into larger consecutive pin groups.
467 
468  :param hal_py.Netlist nl: The netlist to operate on.
469  :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.
470  :returns: The created modules on success, an empty list otherwise.
471  :rtype: list[hal_py.Module]
472  )");
473 
474  m.def(
475  "create_nets_at_unconnected_pins",
476  [](Netlist* nl) -> std::vector<Net*> {
478  if (res.is_ok())
479  {
480  return res.get();
481  }
482  else
483  {
484  log_error("python_context", "{}", res.get_error().get());
485  return {};
486  }
487  },
488  py::arg("nl"),
489  R"(
490  Create a new net for every unconnected output pin of every gate of the netlist.
491  The new nets are named ``HAL_UNCONNECTED_<net_id>``.
492 
493  :param hal_py.Netlist nl: The netlist to operate on.
494  :returns: The created nets on success, an empty list otherwise.
495  :rtype: list[hal_py.Net]
496  )");
497 
498  m.def(
499  "unify_ff_outputs",
500  [](Netlist* nl, const std::vector<Gate*>& ffs = {}, GateType* inverter_type = nullptr) -> std::optional<u32> {
501  auto res = netlist_preprocessing::unify_ff_outputs(nl, ffs, inverter_type);
502  if (res.is_ok())
503  {
504  return res.get();
505  }
506  else
507  {
508  log_error("python_context", "{}", res.get_error().get());
509  return std::nullopt;
510  }
511  },
512  py::arg("nl"),
513  py::arg("ffs") = std::vector<Gate*>(),
514  py::arg("inverter_type") = nullptr,
515  R"(
516  Iterates all flip-flops of the netlist or specified by the user.
517  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.
518  Finally, the ``neg_state`` output net is disconnected from the ``neg_state`` pin and re-connected to the new inverter gate's output.
519 
520  :param hal_py.Netlist nl: The netlist to operate on.
521  :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.
522  :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.
523  :returns: The number of rerouted ``neg_state`` outputs on success, ``None`` otherwise.
524  :rtype: int or ``None``
525  )");
526 
527 #ifndef PYBIND11_MODULE
528  return m.ptr();
529 #endif // PYBIND11_MODULE
530  }
531 } // 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< u32 > remove_unconnected_gates(Netlist *nl)
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 > simplify_lut_inits(Netlist *nl)
Result< std::vector< Net * > > create_nets_at_unconnected_pins(Netlist *nl)
Result< u32 > remove_redundant_loops(Netlist *nl)
Result< u32 > remove_redundant_logic_trees(Netlist *nl)
Result< u32 > reconstruct_top_module_pin_groups(Netlist *nl)
Result< u32 > remove_redundant_gates(Netlist *nl, const std::function< bool(const Gate *)> &filter=nullptr)
Result< u32 > remove_buffers(Netlist *nl)
Result< u32 > propagate_constants(Netlist *nl)
Result< u32 > remove_unused_lut_inputs(Netlist *nl)
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)
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.