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