HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
lattice_ice40.cpp
Go to the documentation of this file.
2 
3 #include "hal_core/defines.h"
12 
13 #include <vector>
14 
15 namespace hal
16 {
17  namespace module_identification
18  {
19  namespace lattice_ice40
20  {
21  namespace {
31  std::vector<std::unique_ptr<BaseCandidate>> find_carry_chains(const Netlist* nl)
32  {
33  std::vector<std::vector<Gate*>> carry_chains;
34 
35  // retrieve all carry gates
36  std::vector<Gate*> carry_gates = nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::c_carry); });
37  std::set<Gate*> carry_gates_set = std::set<Gate*>(carry_gates.begin(), carry_gates.end());
38 
39  // collect carry chains until all carry gates have been analyzed
40  while (!carry_gates_set.empty())
41  {
42  Gate* current_gate = *carry_gates_set.begin();
43  const GateType* carry_type = current_gate->get_type();
44 
45  // get carry chains by defining appropriate filter function
46  auto chain_res = netlist_utils::get_gate_chain(current_gate, {carry_type->get_pin_by_name("CI")}, {carry_type->get_pin_by_name("CO")});
47  if (chain_res.is_error())
48  {
49  return std::vector<std::unique_ptr<BaseCandidate>>();
50  }
51  std::vector<Gate*> carry_chain = chain_res.get();
52 
53  // remove shift register gates from candidate set
54  for (Gate* g : carry_chain)
55  {
56  carry_gates_set.erase(g);
57  }
58 
59  // only consider carry chains with more than 2 gates for now
60  if (carry_chain.size() >= 2)
61  {
62  carry_chains.push_back(carry_chain);
63  }
64  }
65 
66  // check whether a carry chain is a subset of another one
67  std::vector<std::vector<Gate*>> filtered_carry_chains;
68  for (u32 i = 0; i < carry_chains.size(); i++)
69  {
70  auto& c_test = carry_chains.at(i);
71  std::set<Gate*> test_set = {c_test.begin(), c_test.end()};
72 
73  bool is_subset = false;
74  for (u32 j = 0; j < carry_chains.size(); j++)
75  {
76  if (i == j)
77  {
78  continue;
79  }
80 
81  auto& c_other = carry_chains.at(j);
82  std::set<Gate*> other_set = {c_other.begin(), c_other.end()};
83 
84  if (std::includes(other_set.begin(), other_set.end(), test_set.begin(), test_set.end()))
85  {
86  is_subset = true;
87  break;
88  }
89  }
90 
91  if (!is_subset)
92  {
93  filtered_carry_chains.push_back(c_test);
94  }
95  }
96 
97  std::vector<std::unique_ptr<BaseCandidate>> base_candidates;
98  for (const auto& carry_chain : filtered_carry_chains)
99  {
100  std::unique_ptr<BaseCandidate> base_candidate = std::make_unique<BaseCandidate>(carry_chain);
101  base_candidates.emplace_back(std::move(base_candidate));
102  }
103 
104  return base_candidates;
105  }
106 
116  std::vector<std::vector<Gate*>> generate_carry_chain_variants(const BaseCandidate* base_candidate, const std::vector<std::vector<Gate*>>& all_base_candidates_gates)
117  {
118  std::set<u32> possible_starting_points = {0};
119  for (u32 idx = 1; idx < base_candidate->m_gates.size(); idx++)
120  {
121  // check whether gate has constant input and the next one does not
122  if (idx < (base_candidate->m_gates.size() - 1))
123  {
124  const auto& g = base_candidate->m_gates.at(idx);
125  const auto& constant_inputs = g->get_fan_in_nets([](const auto& n) { return n->is_gnd_net() || n->is_vcc_net(); });
126 
127  const auto& g_next = base_candidate->m_gates.at(idx + 1);
128  const auto& constant_inputs_next = g_next->get_fan_in_nets([](const auto& n) { return n->is_gnd_net() || n->is_vcc_net(); });
129 
130  if (!constant_inputs.empty() && constant_inputs_next.empty())
131  {
132  possible_starting_points.insert(idx);
133  possible_starting_points.insert(idx + 1);
134  }
135  }
136 
137  // check whether gate is part of multiple base candidates
138  if (idx < (base_candidate->m_gates.size() - 1))
139  {
140  const auto& g_curr = base_candidate->m_gates.at(idx);
141 
142  bool is_part_of_multiple_base_candidates = false;
143  for (const auto& bc_gates : all_base_candidates_gates)
144  {
145  if (bc_gates == base_candidate->m_gates)
146  {
147  continue;
148  }
149 
150  if (std::find(bc_gates.begin(), bc_gates.end(), g_curr) != bc_gates.end())
151  {
152  is_part_of_multiple_base_candidates = true;
153  break;
154  }
155  }
156 
157  if (is_part_of_multiple_base_candidates)
158  {
159  possible_starting_points.insert(idx + 1);
160  }
161  }
162 
163  // check whether carry gate has successors outside of the carry chain, opposite to its predecessor
164  const auto& g_prev = base_candidate->m_gates.at(idx - 1);
165  const auto& g_curr = base_candidate->m_gates.at(idx);
166 
167  bool prev_has_outside_succ = !g_prev
168  ->get_successors([&](const auto _gp, auto ep) {
169  UNUSED(_gp);
170  return std::find(base_candidate->m_gates.begin(), base_candidate->m_gates.end(), ep->get_gate()) == base_candidate->m_gates.end();
171  })
172  .empty();
173  bool curr_has_outside_succ = !g_curr
174  ->get_successors([&](const auto _gp, auto ep) {
175  UNUSED(_gp);
176  return std::find(base_candidate->m_gates.begin(), base_candidate->m_gates.end(), ep->get_gate()) == base_candidate->m_gates.end();
177  })
178  .empty();
179 
180  if (curr_has_outside_succ && !prev_has_outside_succ)
181  {
182  possible_starting_points.insert(idx);
183  }
184  }
185 
186  // finding too many starting points is computationally infeasible
187  if (possible_starting_points.size() > 4)
188  {
189  return {base_candidate->m_gates};
190  }
191 
192  std::vector<std::vector<Gate*>> carry_chain_variants;
193  for (const auto& idx : possible_starting_points)
194  {
195  carry_chain_variants.push_back({base_candidate->m_gates.begin() + idx, base_candidate->m_gates.end()});
196  }
197 
198  return carry_chain_variants;
199  }
200 
201 
212  std::vector<std::unique_ptr<StructuralCandidate>> build_structural_candidates(BaseCandidate* base_candidate, const std::vector<Gate*>& carry_chain)
213  {
214  std::vector<std::unique_ptr<StructuralCandidate>> candidates;
215 
216  // NOTE add candidate that includes all combinational candidates in front of the carry gates
217  {
218  std::set<Gate*> all_comb_gates = {};
219  for (const auto& g : carry_chain)
220  {
221  const auto next_gates = netlist_utils::get_next_gates(g, false, 0, [](const auto ng) {
222  return ng->get_type()->has_property(GateTypeProperty::combinational) && !ng->get_type()->has_property(GateTypeProperty::c_carry)
223  && !ng->get_type()->has_property(GateTypeProperty::ground) && !ng->get_type()->has_property(GateTypeProperty::power);
224  });
225  all_comb_gates.insert(g);
226  all_comb_gates.insert(next_gates.begin(), next_gates.end());
227  }
228 
229  std::unique_ptr<StructuralCandidate> all_comb_candidate = std::make_unique<StructuralCandidate>(base_candidate, utils::to_vector(all_comb_gates));
230  if (all_comb_gates.size() <= (base_candidate->m_gates.size() * 16))
231  {
232  candidates.emplace_back(std::move(all_comb_candidate));
233  }
234  }
235 
236  // NOTE Try to build a candidate that adds the successors of the individual carry gates.
237  // Afterwards propagate back from those gates to include all combinational predecessors
238  {
239  std::vector<Gate*> starting_gates = carry_chain;
240  std::set<Gate*> successor_gates;
241  for (const auto& sg : starting_gates)
242  {
243  for (const auto& ep : sg->get_successors())
244  {
245  if (ep != nullptr && ep->get_gate()->get_type()->has_property(GateTypeProperty::combinational) && !ep->get_gate()->get_type()->has_property(GateTypeProperty::c_carry))
246  {
247  successor_gates.insert(ep->get_gate());
248  }
249  }
250  }
251 
252  starting_gates.insert(starting_gates.end(), successor_gates.begin(), successor_gates.end());
253  std::set<Gate*> all_comb_gates = {};
254  for (const auto& g : starting_gates)
255  {
256  const auto next_gates = netlist_utils::get_next_gates(g, false, 0, [](const auto ng) {
257  return ng->get_type()->has_property(GateTypeProperty::combinational) && !ng->get_type()->has_property(GateTypeProperty::c_carry)
258  && !ng->get_type()->has_property(GateTypeProperty::ground) && !ng->get_type()->has_property(GateTypeProperty::power);
259  });
260  all_comb_gates.insert(g);
261  all_comb_gates.insert(next_gates.begin(), next_gates.end());
262  }
263  // all_comb_candidate_ext->m_gates = utils::to_vector(all_comb_gates);
264 
265  std::unique_ptr<StructuralCandidate> all_comb_candidate_ext = std::make_unique<StructuralCandidate>(base_candidate, utils::to_vector(all_comb_gates));
266  if (all_comb_gates.size() <= (base_candidate->m_gates.size() * 8))
267  {
268  candidates.emplace_back(std::move(all_comb_candidate_ext));
269  }
270  }
271 
272  Gate* first_carry = carry_chain.front();
273  Gate* last_carry = carry_chain.back();
274 
275  std::vector<std::vector<Gate*>> candidate_gate_variants = {carry_chain};
276  std::vector<std::vector<Gate*>> candidate_gate_variants_without_last_gate = {carry_chain};
277 
278  // check if first gate is carry
279  if (!first_carry->get_type()->has_property(GateTypeProperty::c_carry))
280  {
281  return candidates;
282  }
283  log_info("module_identification", "\tcarry_chain: {}", first_carry->get_name());
284 
285  // get the other successors of the carry gates that are not part of the carry chain.
286  // this can introduce variants if there are multiple additional successors.
287  for (const auto& gate : carry_chain)
288  {
289  std::vector<std::vector<Gate*>> new_candidate_gate_variants;
290  std::vector<std::vector<Gate*>> new_candidate_gate_variants_without_last_gate;
291 
292  if ((gate != last_carry) && (gate->get_successors().size() > 4))
293  {
294  return candidates;
295  }
296 
297  for (const auto& suc_endp : gate->get_successors())
298  {
299  auto suc_gate = suc_endp->get_gate();
300 
301  if (suc_gate->get_type()->has_property(hal::GateTypeProperty::io))
302  {
303  log_error("module_identification", "\tfound IO gate, but why?");
304  }
305  else if (suc_gate->get_type()->has_property(hal::GateTypeProperty::sequential))
306  {
307  continue;
308  }
309  else if (!suc_gate->get_type()->has_property(GateTypeProperty::c_carry))
310  {
311  // for each successor create a new candidate variant
312  for (const auto& cgv : candidate_gate_variants)
313  {
314  if (std::find(cgv.begin(), cgv.end(), suc_gate) != cgv.end())
315  {
316  continue;
317  }
318 
319  auto new_cgv = cgv;
320  new_cgv.push_back(suc_gate);
321 
322  new_candidate_gate_variants.push_back(new_cgv);
323  }
324 
325  if (new_candidate_gate_variants.size() > 4)
326  {
327  return candidates;
328  }
329 
330  if (gate != last_carry)
331  {
332  new_candidate_gate_variants_without_last_gate = new_candidate_gate_variants;
333  }
334  else
335  {
336  new_candidate_gate_variants_without_last_gate = candidate_gate_variants_without_last_gate;
337  }
338  }
339  }
340 
341  if (!new_candidate_gate_variants.empty())
342  {
343  candidate_gate_variants = new_candidate_gate_variants;
344  candidate_gate_variants_without_last_gate = new_candidate_gate_variants_without_last_gate;
345  }
346  }
347 
348  for (const auto& cgv : candidate_gate_variants)
349  {
350  std::set<Gate*> unique_gates = {cgv.begin(), cgv.end()};
351  std::vector<Gate*> cgv_filtered = {unique_gates.begin(), unique_gates.end()};
352 
353  candidates.push_back(std::make_unique<StructuralCandidate>(base_candidate, cgv_filtered));
354  }
355 
356  for (const auto& cgv : candidate_gate_variants_without_last_gate)
357  {
358  std::set<Gate*> unique_gates = {cgv.begin(), cgv.end()};
359  std::vector<Gate*> cgv_filtered = {unique_gates.begin(), unique_gates.end()};
360 
361  candidates.push_back(std::make_unique<StructuralCandidate>(base_candidate, cgv_filtered));
362  }
363 
364  // Many arithmetic operations require one or two gates that are not successors of the carry chain but instead share an input with the first gate of the chain
365  auto input_nets_first_gate = first_carry->get_fan_in_nets([](Net* net) { return !(net->is_gnd_net() || net->is_vcc_net()); });
366 
367  std::vector<Gate*> total_successor_gates;
368  std::vector<Gate*> total_reduced_successor_gates;
369  std::vector<Gate*> total_more_reduced_successor_gates;
370 
371  for (const auto input_net : input_nets_first_gate)
372  {
373  auto successor_enpoints = input_net->get_destinations();
374  for (const auto& ep : successor_enpoints)
375  {
376  auto gate = ep->get_gate();
377  if (gate == nullptr)
378  {
379  continue;
380  }
381 
382  if (gate->get_type()->has_property(GateTypeProperty::sequential))
383  {
384  continue;
385  }
386 
387  if (gate->get_type()->has_property(GateTypeProperty::c_carry))
388  {
389  continue;
390  }
391 
392  if (gate->get_type()->has_property(hal::GateTypeProperty::io))
393  {
394  continue;
395  }
396 
397  if (!gate->get_type()->has_property(hal::GateTypeProperty::combinational))
398  {
399  continue;
400  }
401 
402  total_successor_gates.push_back(gate);
403 
404  const auto gt = gate->get_type();
405 
406  // bool is_inv_xor_xnor = gt->has_property(GateTypeProperty::c_inverter) || gt->has_property(GateTypeProperty::c_xor) || gt->has_property(GateTypeProperty::c_xnor);
407  // bool is_single_input_lut = gt->has_property(GateTypeProperty::c_lut) && (gate->get_fan_in_nets([](const auto& n) { return !n->is_gnd_net() && !n->is_vcc_net(); }).size() == 1);
408  bool is_not_four_input = gate->get_fan_in_nets([](Net* net) { return !(net->is_gnd_net() || net->is_vcc_net()); }).size() < 4;
409 
410  bool to_be_considered = is_not_four_input;
411 
412  if (!to_be_considered)
413  {
414  continue;
415  }
416 
417  total_reduced_successor_gates.push_back(gate);
418 
419  bool is_not_ao = !(gt->has_property(GateTypeProperty::c_and) || gt->has_property(GateTypeProperty::c_or));
420  bool is_not_three_input = gate->get_fan_in_nets([](Net* net) { return !(net->is_gnd_net() || net->is_vcc_net()); }).size() < 3;
421 
422  bool to_be_more_considered = is_not_three_input && is_not_ao;
423 
424  if (!to_be_more_considered)
425  {
426  continue;
427  }
428 
429  total_more_reduced_successor_gates.push_back(gate);
430  }
431  }
432 
433  std::set<std::vector<Gate*>> possible_first_gates;
434 
435  const auto considered_gates =
436  ((total_successor_gates.size() > 16) ? ((total_reduced_successor_gates.size() > 16) ? total_more_reduced_successor_gates : total_reduced_successor_gates) : total_successor_gates);
437 
438  for (const auto& gate : considered_gates)
439  {
440  auto nets = gate->get_fan_in_nets([](Net* net) { return !(net->is_gnd_net() || net->is_vcc_net()); });
441  bool consider_gate = true;
442  for (auto const& mandatory_input_net : input_nets_first_gate)
443  {
444  if (std::find(nets.begin(), nets.end(), mandatory_input_net) == nets.end())
445  {
446  consider_gate = false;
447  break;
448  }
449  }
450  if (!consider_gate)
451  {
452  continue;
453  }
454 
455  possible_first_gates.insert({gate});
456 
457  // check if first gate has inverter infront:
458  auto pres_first_gate = gate->get_predecessors();
459  std::vector<Gate*> new_inv_first_gates;
460  for (const auto& pre_first_gate : pres_first_gate)
461  {
462  if (pre_first_gate->get_gate()->get_type()->has_property(GateTypeProperty::c_inverter))
463  {
464  new_inv_first_gates.emplace_back(pre_first_gate->get_gate());
465  }
466  }
467 
468  for (const auto& inv_gate : new_inv_first_gates)
469  {
470  possible_first_gates.insert({gate, inv_gate});
471  }
472 
473  // second first gate
474  for (const auto& gate2 : total_reduced_successor_gates)
475  {
476  if (gate == gate2)
477  {
478  continue;
479  }
480 
481  possible_first_gates.insert({gate, gate2});
482  }
483  }
484 
485  // relaxed rule: if we did not find a gate that has all inputs of the carry, we relax our rule...
486 
487  // check if first carry has inverter infront:
488  std::vector<Gate*> first_carry_inverters;
489  for (const auto& pre_first_carry : first_carry->get_predecessors())
490  {
491  if (pre_first_carry->get_gate()->get_type()->has_property(GateTypeProperty::c_inverter))
492  {
493  first_carry_inverters.emplace_back(pre_first_carry->get_gate());
494  }
495  }
496 
497  if (possible_first_gates.empty())
498  {
499  for (const auto& gate : considered_gates)
500  {
501  possible_first_gates.insert({gate});
502 
503  for (const auto& inv_gate : first_carry_inverters)
504  {
505  possible_first_gates.insert({gate, inv_gate});
506  }
507 
508  // add second level candidate, if only one suc that is combinational
509  auto successors = gate->get_successors();
510  if (successors.size() != 1)
511  {
512  continue;
513  }
514  auto suc_gate = successors.front()->get_gate();
515  if (suc_gate->get_type()->has_property(hal::GateTypeProperty::io))
516  {
517  continue;
518  }
519  if (suc_gate->get_type()->has_property(GateTypeProperty::combinational))
520  {
521  possible_first_gates.insert({gate, suc_gate});
522  }
523  }
524  }
525 
526  std::vector<std::vector<Gate*>> new_candidate_gate_variants;
527  for (const auto& c : candidates)
528  {
529  for (const auto& add_gates : possible_first_gates)
530  {
531  auto new_gates = c->m_gates;
532  new_gates.insert(new_gates.end(), add_gates.begin(), add_gates.end());
533  new_candidate_gate_variants.push_back(new_gates);
534  }
535  }
536 
537  for (auto& new_cgv : new_candidate_gate_variants)
538  {
539  candidates.push_back(std::make_unique<StructuralCandidate>(base_candidate, new_cgv));
540  }
541 
542  std::vector<std::unique_ptr<StructuralCandidate>> new_inverter_candidates;
543 
544  // add inverters for substracts
545  for (const auto& candidate : candidates)
546  {
547  u32 inverter_counter = 0;
548  u32 carry_counter = 0;
549  std::unique_ptr<StructuralCandidate> new_candidate = std::make_unique<StructuralCandidate>(base_candidate, candidate->m_gates);
550  for (const auto& gate : candidate->m_gates)
551  {
552  if (!gate->get_type()->has_property(hal::GateTypeProperty::c_carry))
553  {
554  auto suc_endpoints = gate->get_successors();
555  std::vector<Gate*> inverter_successors;
556  for (const auto& suc_endpoint : suc_endpoints)
557  {
558  auto suc_gate = suc_endpoint->get_gate();
559  if (suc_gate->get_type()->has_property(hal::GateTypeProperty::c_inverter))
560  {
561  inverter_successors.push_back(suc_gate);
562  }
563  }
564 
565  if (inverter_successors.size() > 2)
566  {
567  continue;
568  }
569 
570  for (const auto& is : inverter_successors)
571  {
572  inverter_counter++;
573  new_candidate->m_gates.push_back(is);
574  }
575  }
576  else
577  {
578  carry_counter++;
579  }
580  }
581  if (inverter_counter >= carry_counter - 1)
582  {
583  new_inverter_candidates.emplace_back(std::move(new_candidate));
584  }
585  }
586 
587  for (auto& ic : new_inverter_candidates)
588  {
589  candidates.push_back(std::move(ic));
590  }
591 
592  {
593  std::vector<std::unique_ptr<StructuralCandidate>> new_candidates;
594 
595  // TODO: Probably redo this
596  // add weird last gates
597  for (const auto& candidate : candidates)
598  {
599  Gate* last_gate = candidate->m_gates.back();
600 
601  for (const auto& pred_endp : last_gate->get_predecessors())
602  {
603  Gate* pred_gate = pred_endp->get_gate();
604  if (pred_gate->get_type()->has_property(hal::GateTypeProperty::c_carry) || !pred_gate->get_type()->has_property(hal::GateTypeProperty::combinational))
605  {
606  continue;
607  }
608 
609  std::unique_ptr<StructuralCandidate> new_candidate = std::make_unique<StructuralCandidate>(base_candidate, candidate->m_gates);
610  new_candidate->m_gates.emplace_back(pred_gate);
611  new_candidates.emplace_back(std::move(new_candidate));
612  }
613  }
614  for (auto& nc : new_candidates)
615  {
616  candidates.push_back(std::move(nc));
617  }
618  }
619 
620  // TODO this should not be happening, need to investigate this in the future
621  // delete all gnd and vcc gates from candidates
622  for (auto& cand : candidates)
623  {
624  if (!std::any_of(cand->m_gates.begin(), cand->m_gates.end(), [](const auto& g) { return g->is_gnd_gate() || g->is_vcc_gate(); }))
625  {
626  continue;
627  }
628 
629 
630  cand->m_gates.erase(std::remove_if(cand->m_gates.begin(),
631  cand->m_gates.end(),
632  [](const auto& g) {
633  return g->is_gnd_gate() || g->is_vcc_gate(); // put your condition here
634  }),
635  cand->m_gates.end());
636 
637  }
638 
639  // remove same candidates
640  std::set<std::set<Gate*>> all_sets;
641  u32 old_size = candidates.size();
642  std::vector<std::unique_ptr<StructuralCandidate>> candidates_to_return;
643 
644  for (auto& cand : candidates)
645  {
646  std::set<Gate*> set_gates = {cand->m_gates.begin(), cand->m_gates.end()};
647  if (all_sets.find(set_gates) == all_sets.end())
648  {
649  all_sets.insert(set_gates);
650  candidates_to_return.push_back(std::make_unique<StructuralCandidate>(base_candidate, std::vector<Gate*>{set_gates.begin(), set_gates.end()}));
651  }
652  }
653 
654  log_info("module_identification", "\tremaining {}/{} after double candidate removal", candidates_to_return.size(), old_size);
655  return candidates_to_return;
656  }
657  }
658 
659  std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> generate_structural_candidates(const Netlist* nl)
660  {
661  std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> base_to_structural_candidates;
662 
663  auto base_candidates = find_carry_chains(nl);
664 
665  std::vector<std::vector<Gate*>> all_base_candidates_gates;
666  for (const auto& bc : base_candidates)
667  {
668  all_base_candidates_gates.push_back(bc->m_gates);
669  }
670 
671  log_info("module_identification", "found {} carry chains, building structural variants now...", base_candidates.size());
672 
673  for (auto& base_candidate : base_candidates)
674  {
675  const auto& carry_chain_variants = generate_carry_chain_variants(base_candidate.get(), all_base_candidates_gates);
676 
677  std::vector<std::unique_ptr<StructuralCandidate>> structural_candidates;
678  for (const auto& ccv : carry_chain_variants)
679  {
680  auto new_candidates = build_structural_candidates(base_candidate.get(), ccv);
681  for (auto& nc : new_candidates)
682  {
683  structural_candidates.push_back(std::move(nc));
684  }
685  }
686 
687  base_to_structural_candidates.push_back({std::move(base_candidate), std::move(structural_candidates)});
688  }
689 
690  return base_to_structural_candidates;
691  }
692  } // namespace lattice_ice40
693  } // namespace module_identification
694 } // namespace hal
This file contains the definition of the BaseCandidate class, which represents a base candidate in th...
u32 size
uint32_t u32
Definition: defines.h:41
#define UNUSED(expr)
Definition: defines.h:49
#define log_error(channel,...)
Definition: log.h:78
#define log_info(channel,...)
Definition: log.h:70
This file contains the function to generate structural candidates for Lattice iCE40 FPGAs.
std::vector< std::pair< std::unique_ptr< BaseCandidate >, std::vector< std::unique_ptr< StructuralCandidate > > > > generate_structural_candidates(const Netlist *nl)
Generate structural candidates for a given netlist.
@ lattice_ice40
Lattice iCE40 FPGA architecture.
Result< std::vector< Gate * > > get_gate_chain(Gate *start_gate, const std::vector< const GatePin * > &input_pins={}, const std::vector< const GatePin * > &output_pins={}, const std::function< bool(const Gate *)> &filter=nullptr)
std::vector< Gate * > get_next_gates(const Gate *gate, bool get_successors, int depth=0, const std::function< bool(const Gate *)> &filter=nullptr)
bool is_subset(const T1 &subset, const T2 &superset)
Definition: utils.h:541
std::vector< T > to_vector(const Container< T, Args... > &container)
Definition: utils.h:515
Definition: defines.h:45
Net * net
This file contains the class for defining and managing structural candidates within the module identi...