HAL  v4.5.0-254-g8f9f6a423
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
preprocessing.cpp
Go to the documentation of this file.
4 #include "hal_core/netlist/net.h"
7 
8 #include <functional>
9 
10 namespace
11 {
12  // Extract the numeric index from an address pin name.
13  // Handles both "Ai" (e.g. "A0", "A3") and "A(i)" (e.g. "A(0)", "A(4)") formats.
14  u32 srl_addr_pin_index(const hal::GatePin* p)
15  {
16  std::string digits;
17  for (unsigned char c : p->get_name())
18  {
19  if (c >= '0' && c <= '9')
20  {
21  digits += static_cast<char>(c);
22  }
23  }
24  return static_cast<u32>(std::stoull(digits));
25  }
26 
27  // Returns the fixed cascade output pin name for cascadeable SRL types, or empty string.
28  // SRLC16E always outputs stage 15 on Q15; SRLC32E always outputs stage 31 on Q31.
29  std::string srl_cascade_pin(const std::string& type_name)
30  {
31  if (type_name == "SRLC16E")
32  {
33  return "Q15";
34  }
35  if (type_name == "SRLC32E")
36  {
37  return "Q31";
38  }
39  return {};
40  }
41 
42  bool is_srl_type(const std::string& type_name)
43  {
44  return type_name == "SRL16E" || type_name == "SRL16" || type_name == "SRLC16E" || type_name == "SRLC32E";
45  }
46 } // namespace
47 
48 namespace hal
49 {
50  namespace xilinx_toolbox
51  {
52  namespace
53  {
59  std::vector<Gate*> gates_in_scope(const Netlist* nl, const std::vector<Gate*>& gates, const std::function<bool(const Gate*)>& type_filter)
60  {
61  if (gates.empty())
62  {
63  return nl->get_gates(type_filter);
64  }
65 
66  std::vector<Gate*> res;
67  for (auto* g : gates)
68  {
69  if (type_filter(g))
70  {
71  res.push_back(g);
72  }
73  }
74  return res;
75  }
76  } // namespace
77 
78  Result<std::monostate> split_lut(Gate* g, bool create_module)
79  {
80  if (g->get_type()->get_name() != "LUT6_2")
81  {
82  return ERR("gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " is not of type LUT6_2");
83  }
84 
85  Netlist* nl = g->get_netlist();
86  auto* lut5_type = nl->get_gate_library()->get_gate_type_by_name("LUT5");
87  auto* lut6_type = nl->get_gate_library()->get_gate_type_by_name("LUT6");
88 
89  auto* o5 = g->get_fan_out_net("O5");
90  auto* o6 = g->get_fan_out_net("O6");
91 
92  // Determine target module for replacement gates.
93  Module* target_mod = nullptr;
94  if (create_module)
95  {
96  target_mod = nl->create_module(g->get_name(), g->get_module());
97  }
98  else if (!g->get_module()->is_top_module())
99  {
100  target_mod = g->get_module();
101  }
102 
103  // LUT6_2 uses a 64-bit INIT string (16 hex chars): O6 uses all 64 bits,
104  // O5 uses bits [0, 31] (the lower half, i.e., truth table for I5=0).
105  auto* lc_g = g->get_type()->get_component_as<LUTComponent>([](const GateTypeComponent* c) { return c->get_type() == GateTypeComponent::ComponentType::lut; });
106  if (lc_g == nullptr)
107  {
108  return ERR("LUT6_2 gate type has no LUT component");
109  }
110  auto* lc_lut5t = (lut5_type != nullptr) ? lut5_type->get_component_as<LUTComponent>([](const GateTypeComponent* c) { return c->get_type() == GateTypeComponent::ComponentType::lut; }) : nullptr;
111  auto* lc_lut6t = (lut6_type != nullptr) ? lut6_type->get_component_as<LUTComponent>([](const GateTypeComponent* c) { return c->get_type() == GateTypeComponent::ComponentType::lut; }) : nullptr;
112  if (o5 != nullptr && o5->get_num_of_destinations() > 0)
113  {
114  const auto init_O5_res = lc_g->get_init_string(g, "O5");
115  if (init_O5_res.is_error())
116  {
117  log_warning("xilinx_toolbox", "could not get INIT string for O5 of gate '{}' with ID {}, skipping O5 split.", g->get_name(), g->get_id());
118  }
119  else
120  {
121  const auto init_O5 = init_O5_res.get();
122  auto* lut5 = nl->create_gate(lut5_type, g->get_name() + "_split_O5");
123  lut5->set_data("xilinx_preprocessing_information", "original_init", "string", init_O5);
124 
125  if (lc_lut5t == nullptr || lc_lut5t->set_init_string(lut5, "O", init_O5).is_error())
126  {
127  log_warning("xilinx_toolbox", "could not set INIT string of gate '{}' with ID {}.", lut5->get_name(), lut5->get_id());
128  nl->delete_gate(lut5);
129  }
130  else
131  {
132  if (target_mod != nullptr)
133  {
134  target_mod->assign_gate(lut5);
135  }
136 
137  // I5 selects the upper/lower half of the INIT; LUT5 only uses the lower
138  // half (I5=0), so I5 is not a real input and is excluded here.
139  for (const auto& in_ep : g->get_fan_in_endpoints())
140  {
141  if (in_ep->get_pin()->get_name() == "I5")
142  {
143  continue;
144  }
145 
146  in_ep->get_net()->add_destination(lut5, in_ep->get_pin()->get_name());
147  }
148 
149  o5->add_source(lut5, "O");
150  }
151  }
152  }
153 
154  if (o6 != nullptr && o6->get_num_of_destinations() > 0)
155  {
156  const auto init_O6_res = lc_g->get_init_string(g, "O6");
157  if (init_O6_res.is_error())
158  {
159  return ERR("could not get O6 INIT string of gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
160  }
161  else
162  {
163  const auto init_O6 = init_O6_res.get();
164  auto* lut6 = nl->create_gate(lut6_type, g->get_name() + "_split_O6");
165  lut6->set_data("xilinx_preprocessing_information", "original_init", "string", init_O6);
166 
167  // O6 is a full 6-input LUT and uses the entire 64-bit INIT string.
168  if (lc_lut6t == nullptr || lc_lut6t->set_init_string(lut6, "O", init_O6).is_error())
169  {
170  log_warning("xilinx_toolbox", "could not set INIT string of gate '{}' with ID {}.", lut6->get_name(), lut6->get_id());
171  nl->delete_gate(lut6);
172  }
173  else
174  {
175  if (target_mod != nullptr)
176  {
177  target_mod->assign_gate(lut6);
178  }
179 
180  for (const auto& in_ep : g->get_fan_in_endpoints())
181  {
182  in_ep->get_net()->add_destination(lut6, in_ep->get_pin()->get_name());
183  }
184 
185  o6->add_source(lut6, "O");
186  }
187  }
188  }
189 
190  // Always delete the original LUT6_2 regardless of which outputs were used.
191  if (!nl->delete_gate(g))
192  {
193  return ERR("failed to delete gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
194  }
195 
196  return OK({});
197  }
198 
199  Result<u32> split_luts(const std::vector<Gate*>& gates, bool create_module)
200  {
201  u32 count = 0;
202  for (auto* g : gates)
203  {
204  if (auto res = split_lut(g, create_module); res.is_error())
205  {
206  log_warning("xilinx_toolbox", "skipping gate '{}' with ID {}: {}", g->get_name(), g->get_id(), res.get_error().get());
207  }
208  else
209  {
210  count++;
211  }
212  }
213  log_info("xilinx_toolbox", "split {} of {} LUT6_2 gates into LUT6 and LUT5 gates", count, gates.size());
214  return OK(count);
215  }
216 
217  Result<u32> split_luts(Netlist* nl, const std::vector<Gate*>& gates, bool create_module)
218  {
219  return split_luts(gates_in_scope(nl, gates, [](const Gate* g) { return g->get_type()->get_name() == "LUT6_2"; }), create_module);
220  }
221 
223  {
224  const std::string type_name = g->get_type()->get_name();
225  if (!is_srl_type(type_name))
226  {
227  return ERR("gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " is not a supported shift register type (SRL16, SRL16E, SRLC16E, SRLC32E)");
228  }
229 
230  Netlist* nl = g->get_netlist();
231  GateType* ff_gt = nl->get_gate_library()->get_gate_type_by_name("FDCE");
232  if (ff_gt == nullptr)
233  {
234  return ERR("could not find gate type 'FDCE' in gate library");
235  }
236 
237  // --- Address (control) pins: sort A0 < A1 < ... ---
238  auto ctrl_pins = g->get_type()->get_pins([](const GatePin* p) { return p->get_direction() == PinDirection::input && p->get_type() == PinType::control; });
239 
240  const u32 expected_ctrl = (type_name == "SRLC32E") ? 5u : 4u;
241  if (ctrl_pins.size() != expected_ctrl)
242  {
243  return ERR("gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " has " + std::to_string(ctrl_pins.size()) + " control pins, expected "
244  + std::to_string(expected_ctrl));
245  }
246 
247  std::sort(ctrl_pins.begin(), ctrl_pins.end(), [](const GatePin* a, const GatePin* b) { return srl_addr_pin_index(a) < srl_addr_pin_index(b); });
248 
249  // Compute select_value from constant address pins (binary, little-endian: A0 = bit 0).
250  // Stage select_value is where Q is tapped; depth = select_value + 1 clock cycles.
251  u32 select_value = 0;
252  for (u32 i = 0; i < ctrl_pins.size(); i++)
253  {
254  const Net* cn = g->get_fan_in_net(ctrl_pins[i]);
255  if (cn == nullptr)
256  {
257  return ERR("control pin '" + ctrl_pins[i]->get_name() + "' of gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " has no connected net");
258  }
259  if (!cn->is_gnd_net() && !cn->is_vcc_net())
260  {
261  return ERR("control pin '" + ctrl_pins[i]->get_name() + "' of gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " is not driven by a constant (GND/VCC) net");
262  }
263  if (cn->is_vcc_net())
264  {
265  select_value |= (1u << i);
266  }
267  }
268 
269  // --- Clock / enable / data pins ---
270  const auto clk_pins = g->get_type()->get_pins([](const GatePin* p) { return p->get_direction() == PinDirection::input && p->get_type() == PinType::clock; });
271  if (clk_pins.size() != 1)
272  {
273  return ERR("gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " has " + std::to_string(clk_pins.size()) + " clock pins, expected 1");
274  }
275 
276  const auto en_pins = g->get_type()->get_pins([](const GatePin* p) { return p->get_direction() == PinDirection::input && p->get_type() == PinType::enable; });
277  if (en_pins.size() != 1)
278  {
279  return ERR("gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " has " + std::to_string(en_pins.size()) + " enable pins, expected 1");
280  }
281 
282  const auto data_pins = g->get_type()->get_pins([](const GatePin* p) { return p->get_direction() == PinDirection::input && p->get_type() == PinType::data; });
283  if (data_pins.size() != 1)
284  {
285  return ERR("gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()) + " has " + std::to_string(data_pins.size()) + " data pins, expected 1");
286  }
287 
288  Net* clk_in = g->get_fan_in_net(clk_pins.front());
289  Net* enable_in = g->get_fan_in_net(en_pins.front());
290  Net* data_in = g->get_fan_in_net(data_pins.front());
291 
292  if (clk_in == nullptr)
293  {
294  return ERR("no clock input connected to gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
295  }
296  if (enable_in == nullptr)
297  {
298  return ERR("no enable input connected to gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
299  }
300  if (data_in == nullptr)
301  {
302  return ERR("no data input connected to gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
303  }
304 
305  // --- Output nets ---
306  const std::string casc_pin = srl_cascade_pin(type_name);
307  Net* state_out = g->get_fan_out_net("Q");
308  Net* cascade_out = casc_pin.empty() ? nullptr : g->get_fan_out_net(casc_pin);
309 
310  // Cascade output is only "used" when it has actual downstream consumers.
311  const bool cascade_used = (cascade_out != nullptr && cascade_out->get_num_of_destinations() > 0);
312 
313  if (state_out == nullptr && !cascade_used)
314  {
315  return ERR("no output connected to gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
316  }
317 
318  // When the cascade output is used, all stages up to the maximum must be materialized
319  // (SRLC16E: 0..15, SRLC32E: 0..31) so that Q15/Q31 reflects the true last stage.
320  const u32 max_depth = (1u << ctrl_pins.size()) - 1u;
321  const u32 register_size = cascade_used ? max_depth : select_value;
322 
323  // --- Build the FDE chain ---
324  Module* parent_mod = g->get_module();
325 
326  // Determine target module for replacement FFs.
327  Module* target_mod = nullptr;
328  if (create_module)
329  {
330  target_mod = nl->create_module(g->get_name(), parent_mod);
331  }
332  else if (!parent_mod->is_top_module())
333  {
334  target_mod = parent_mod;
335  }
336 
337  // Pre-extract parameters to copy to each FF (evaluated once, applied per-FF in the chain).
338  // INIT is an N-bit SRL reset value: bit i becomes the 1-bit INIT of FF i.
339  // IS_CLK_INVERTED is 1-bit and the same for all FFs; mapped to IS_C_INVERTED on FDCE.
340  // Both are forwarded silently — no error if either side lacks the declaration.
341  const bool has_clk_inv = g->has_parameter("IS_CLK_INVERTED");
342  const std::string clk_inv_val = has_clk_inv ? g->get_parameter_value("IS_CLK_INVERTED").get() : "";
343 
344  bool has_init = g->has_parameter("INIT");
345  u64 init_bits = 0;
346  if (has_init)
347  {
348  try
349  {
350  init_bits = std::stoull(g->get_parameter_value("INIT").get(), nullptr, 0);
351  }
352  catch (...)
353  {
354  log_warning("xilinx_toolbox", "could not parse INIT of gate '{}' with ID {}, skipping INIT copy", g->get_name(), g->get_id());
355  has_init = false;
356  }
357  }
358 
359  std::vector<Net*> chain_nets; // Q output net of each FF, used to wire D of next
360 
361  for (u32 i = 0; i <= register_size; i++)
362  {
363  const std::string ff_name = g->get_name() + "_split_ff_" + std::to_string(i);
364  Gate* ff = nl->create_gate(ff_gt, ff_name);
365 
366  if (target_mod != nullptr)
367  {
368  target_mod->assign_gate(ff);
369  }
370 
371  if (has_init)
372  {
373  if (auto decl = ff->get_type()->get_parameter("INIT"); decl.is_ok())
374  {
375  const std::string bit_val = ((init_bits >> i) & 1u) ? "0x1" : "0x0";
376  if (ff->set_parameter(decl.get(), bit_val).is_error())
377  {
378  log_warning("xilinx_toolbox", "could not copy INIT bit {} to FF '{}' with ID {}", i, ff->get_name(), ff->get_id());
379  }
380  }
381  }
382  if (has_clk_inv)
383  {
384  if (auto decl = ff->get_type()->get_parameter("IS_C_INVERTED"); decl.is_ok())
385  {
386  if (ff->set_parameter(decl.get(), clk_inv_val).is_error())
387  {
388  log_warning("xilinx_toolbox", "could not copy IS_CLK_INVERTED to IS_C_INVERTED on FF '{}' with ID {}", ff->get_name(), ff->get_id());
389  }
390  }
391  }
392 
393  clk_in->add_destination(ff, "C");
394  enable_in->add_destination(ff, "CE");
395 
396  (i == 0 ? data_in : chain_nets.back())->add_destination(ff, "D");
397 
398  const bool is_q_stage = (state_out != nullptr && i == select_value);
399  const bool is_cascade_stage = (cascade_used && i == register_size);
400 
401  if (is_q_stage)
402  {
403  state_out->add_source(ff, "Q");
404  if (is_cascade_stage)
405  {
406  // Q and cascade tap the same stage: a single FF output pin can source only
407  // one net, so merge cascade_out's consumers into state_out.
408  // Capture gate/pin BEFORE removal since remove_destination frees the Endpoint.
409  std::vector<std::pair<Gate*, std::string>> casc_consumers;
410  for (const Endpoint* ep : cascade_out->get_destinations())
411  {
412  casc_consumers.emplace_back(ep->get_gate(), ep->get_pin()->get_name());
413  }
414  for (const auto& [g, pin] : casc_consumers)
415  {
416  cascade_out->remove_destination(g, pin);
417  state_out->add_destination(g, pin);
418  }
419  }
420  chain_nets.push_back(state_out);
421  }
422  else if (is_cascade_stage)
423  {
424  cascade_out->add_source(ff, "Q");
425  chain_nets.push_back(cascade_out);
426  }
427  else
428  {
429  Net* internal = nl->create_net(ff_name + "_out");
430  internal->add_source(ff, "Q");
431  chain_nets.push_back(internal);
432  }
433  }
434 
435  if (!nl->delete_gate(g))
436  {
437  return ERR("failed to delete gate '" + g->get_name() + "' with ID " + std::to_string(g->get_id()));
438  }
439 
440  return OK({});
441  }
442 
443  Result<u32> split_shift_registers(const std::vector<Gate*>& gates, bool create_module)
444  {
445  u32 count = 0;
446  for (Gate* g : gates)
447  {
448  if (auto res = split_shift_register(g, create_module); res.is_error())
449  {
450  log_warning("xilinx_toolbox", "skipping gate '{}' with ID {}: {}", g->get_name(), g->get_id(), res.get_error().get());
451  }
452  else
453  {
454  count++;
455  }
456  }
457  log_info("xilinx_toolbox", "split {} of {} shift register gates into FDE flip-flop chains", count, gates.size());
458  return OK(count);
459  }
460 
461  Result<u32> split_shift_registers(Netlist* nl, const std::vector<Gate*>& gates, bool create_module)
462  {
463  return split_shift_registers(gates_in_scope(nl, gates, [](const Gate* g) { return is_srl_type(g->get_type()->get_name()); }), create_module);
464  }
465 
466  Result<u32> remove_no_load_wires(Netlist* nl, const std::vector<Net*>& nets)
467  {
468  if (nl == nullptr)
469  {
470  return ERR("cannot remove no load wires: netlist is a nullptr");
471  }
472 
473  // Vivado names a net that only exists to give an unused output pin something to drive 'NLW_<instance>_<pin>_UNCONNECTED'.
474  // When the hierarchy is flattened, the instance is a path, so the prefix either starts the name or follows a '/';
475  // a multi-bit no load wire is written as a vector and its bits carry a trailing index like '(2)' or '[2]'.
476  const auto is_no_load_wire = [](const Net* n) {
477  const std::string& name = n->get_name();
478  const std::string prefix = "NLW_";
479  const std::string suffix = "_UNCONNECTED";
480 
481  if (name.compare(0, prefix.size(), prefix) != 0 && name.find("/" + prefix) == std::string::npos)
482  {
483  return false;
484  }
485 
486  std::string::size_type end = name.size();
487  if (end > 0 && (name[end - 1] == ')' || name[end - 1] == ']'))
488  {
489  const auto open = name.find_last_of("([");
490  if (open == std::string::npos || open + 1 >= end - 1 || name.find_first_not_of("0123456789", open + 1) != end - 1)
491  {
492  return false;
493  }
494  end = open;
495  }
496 
497  return end > prefix.size() + suffix.size() && name.compare(end - suffix.size(), suffix.size(), suffix) == 0;
498  };
499 
500  std::vector<Net*> to_delete;
501  for (Net* n : nets.empty() ? nl->get_nets() : nets)
502  {
503  if (n == nullptr || n->get_netlist() != nl)
504  {
505  return ERR("cannot remove no load wires: a net of the scope does not belong to netlist with ID " + std::to_string(nl->get_id()));
506  }
507 
508  if (is_no_load_wire(n) && n->get_num_of_destinations() == 0 && !n->is_global_output_net())
509  {
510  to_delete.push_back(n);
511  }
512  }
513 
514  u32 deleted_nets = 0;
515  for (Net* n : to_delete)
516  {
517  const std::string name = n->get_name();
518  const u32 id = n->get_id();
519  if (!nl->delete_net(n))
520  {
521  return ERR("cannot remove no load wires: failed to delete net '" + name + "' with ID " + std::to_string(id));
522  }
523  deleted_nets++;
524  }
525 
526  log_info("xilinx_toolbox", "removed {} no load wires", deleted_nets);
527 
528  return OK(deleted_nets);
529  }
530  } // namespace xilinx_toolbox
531 } // namespace hal
const std::string & get_name() const
Definition: base_pin.h:110
PinType get_type() const
Definition: base_pin.h:150
PinDirection get_direction() const
Definition: base_pin.h:130
Result< std::string > get_parameter_value(const std::string &name) const
bool has_parameter(const Parameter &param) const
bool set_data(const std::string &category, const std::string &key, const std::string &data_type, const std::string &value, const bool log_with_info_level=false)
Definition: gate.h:59
Net * get_fan_in_net(const std::string &pin_name) const
Definition: gate.cpp:852
GateType * get_type() const
Definition: gate.cpp:162
Net * get_fan_out_net(const std::string &pin_name) const
Definition: gate.cpp:996
const std::string & get_name() const
Definition: gate.cpp:142
Module * get_module() const
Definition: gate.cpp:211
const std::vector< Endpoint * > & get_fan_in_endpoints() const
Definition: gate.cpp:888
Netlist * get_netlist() const
Definition: gate.cpp:137
u32 get_id() const
Definition: gate.cpp:132
GateType * get_gate_type_by_name(const std::string &name) const
virtual ComponentType get_type() const =0
T * get_component_as(const std::function< bool(const GateTypeComponent *)> &filter=nullptr)
const std::string & get_name() const
Definition: gate_type.cpp:64
T * get_component_as(const std::function< bool(const GateTypeComponent *)> &filter=nullptr) const
Definition: gate_type.h:89
std::vector< GatePin * > get_pins(const std::function< bool(GatePin *)> &filter=nullptr) const
Definition: gate_type.cpp:210
Result< std::string > get_init_string(const Gate *gate, const std::string &pin_name) const
bool is_top_module() const
Definition: module.cpp:321
bool assign_gate(Gate *gate)
Definition: module.cpp:331
Definition: net.h:58
Endpoint * add_destination(Gate *gate, const std::string &pin_name)
Definition: net.cpp:300
Endpoint * add_source(Gate *gate, const std::string &pin_name)
Definition: net.cpp:127
u32 get_num_of_destinations(const std::function< bool(Endpoint *ep)> &filter=nullptr) const
Definition: net.cpp:432
std::vector< Endpoint * > get_destinations(const std::function< bool(Endpoint *ep)> &filter=nullptr) const
Definition: net.cpp:450
bool remove_destination(Gate *gate, const std::string &pin_name)
Definition: net.cpp:343
bool is_vcc_net() const
Definition: net.cpp:479
bool is_gnd_net() const
Definition: net.cpp:474
bool delete_net(Net *net)
Definition: netlist.cpp:345
Net * create_net(const u32 net_id, const std::string &name)
Definition: netlist.cpp:335
bool delete_gate(Gate *gate)
Definition: netlist.cpp:185
u32 get_id() const
Definition: netlist.cpp:77
Gate * create_gate(const u32 gate_id, GateType *gate_type, const std::string &name="", i32 x=-1, i32 y=-1)
Definition: netlist.cpp:175
const std::vector< Net * > & get_nets() const
Definition: netlist.cpp:366
const GateLibrary * get_gate_library() const
Definition: netlist.cpp:134
Module * create_module(const u32 module_id, const std::string &name, Module *parent, const std::vector< Gate * > &gates={})
Definition: netlist.cpp:589
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
#define log_info(channel,...)
Definition: log.h:70
#define log_warning(channel,...)
Definition: log.h:76
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
Result< std::monostate > split_shift_register(Gate *g, bool create_module=false)
Split a single shift register primitive into an equivalent chain of FDCE flip-flops.
Result< u32 > remove_no_load_wires(Netlist *nl, const std::vector< Net * > &nets={})
Remove the "no load wire" nets that Vivado writes for unused output pins.
Result< u32 > split_shift_registers(const std::vector< Gate * > &gates, bool create_module=false)
Split a set of shift register primitives into equivalent FDCE flip-flop chains.
Result< std::monostate > split_lut(Gate *g, bool create_module=false)
Split a single LUT6_2 gate into up to two separate LUT gates.
Result< u32 > split_luts(const std::vector< Gate * > &gates, bool create_module=false)
Split a set of LUT6_2 gates into separate LUT gates.
Definition: defines.h:45
std::string name
This file contains all functions related to the HAL plugin API.