HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
module_identification.cpp
Go to the documentation of this file.
2 
16 
17 #include <deque>
18 #include <mutex>
19 
20 // #define PRINT_THREAD_INFO
21 
22 namespace hal
23 {
24  namespace module_identification
25  {
26  hal::Result<std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>>> generate_structural_candidates(const Netlist* nl,
27  const Configuration& config)
28  {
29  auto gl_name = nl->get_gate_library()->get_name();
30  std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> candidates;
31 
32  if (gl_name == "ICE40ULTRA" || gl_name == "ICE40ULTRA_iPhone" || gl_name == "ICE40ULTRA_WITH_HAL_TYPES")
33  {
34  log_info("module_identification", "generate arithmetic structures for {}", gl_name);
36  }
37  else if (gl_name == "XILINX_UNISIM_WITH_HAL_TYPES" || gl_name == "XILINX_UNISIM")
38  {
39  log_info("module_identification", "generate arithmetic structures for {}", gl_name);
41  }
42  else
43  {
44  return ERR("arithmetic structure generation not available for gate_lib: " + gl_name);
45  }
46 
47  // filter out already classified modules or blocked base candidates
48  std::vector<u32> filtered_indices;
49  std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> filtered_candidates;
50 
51  for (u32 idx = 0; idx < candidates.size(); idx++)
52  {
53  auto& [base_candidate, structural_candidates] = candidates.at(idx);
54  bool filtered_out = false;
55 
56  for (const auto& blocked_base_candidate : config.m_blocked_base_candidates)
57  {
58  const std::set<Gate*> base_candidate_set = {base_candidate->m_gates.begin(), base_candidate->m_gates.end()};
59 
60  if (base_candidate_set == blocked_base_candidate)
61  {
62  filtered_out = true;
63  break;
64  }
65  }
66 
67  for (const auto& already_classified_candidates : config.m_already_classified_candidates)
68  {
69  const std::set<Gate*> already_classified_candidates_set = {already_classified_candidates.begin(), already_classified_candidates.end()};
70 
71  for (const auto& g : base_candidate->m_gates)
72  {
73  if (already_classified_candidates_set.find(g) != already_classified_candidates_set.end())
74  {
75  filtered_out = true;
76  break;
77  }
78  }
79 
80  if (filtered_out)
81  {
82  break;
83  }
84  }
85 
86  if (!filtered_out)
87  {
88  filtered_indices.push_back(idx);
89  }
90  }
91 
92  log_info("module_identification", "Filtered out already classified candidates. Left with {} / {} base candidates", filtered_indices.size(), candidates.size());
93 
94  for (const auto& idx : filtered_indices)
95  {
96  auto& [base_candidate, structural_candidates] = candidates.at(idx);
97  filtered_candidates.push_back({std::move(base_candidate), std::move(structural_candidates)});
98  }
99 
100  for (const auto& [bc, sc_vec] : filtered_candidates)
101  {
102  for (const auto& sc : sc_vec)
103  {
104  // TODO: this is an ugly fix
105  sc->ctx.m_gates = sc->m_gates;
106  }
107  }
108 
109  return OK(std::move(filtered_candidates));
110  }
111 
113  {
114  std::vector<FunctionalCandidate> result;
115 
116  Netlist* nl = sc->m_gates.front()->get_netlist();
117  auto output_nets = get_output_nets(sc->m_gates, false);
118 
119  // TODO make this a config parameter
120  const u32 max_inputs = 130;
121  bool found_oversized_boolean_function = false;
122 
123  for (const auto& o_net : output_nets)
124  {
125  const auto input_nets_res = SubgraphNetlistDecorator(*nl).get_subgraph_function_inputs(sc->m_gates, o_net);
126  if (input_nets_res.is_error())
127  {
128  return ERR_APPEND(input_nets_res.get_error(),
129  "cannot generate functional candidates for " + sc->m_gates.front()->get_name() + ": failed to get subgrapg inputs for net " + o_net->get_name() + " with ID "
130  + std::to_string(o_net->get_id()));
131  }
132  const auto input_net_count = input_nets_res.get().size();
133 
134  if (input_net_count > max_inputs)
135  {
136  found_oversized_boolean_function = true;
137  break;
138  }
139  }
140 
141  if (found_oversized_boolean_function)
142  {
143  return OK({});
144  }
145 
146  // TODO this should not happen, but exists as a safety meassure
147  if (sc->m_gates != sc->ctx.m_gates)
148  {
149  return ERR("cannot generate functional candidates: candiate and context gates are not identical!");
150  }
151 
152  const auto res = sc->ctx.populate_boolean_function_cache(output_nets);
153  if (res.is_error())
154  {
155  return ERR_APPEND(res.get_error(), "cannot generate functional candidates: failed to populate Boolean context cache");
156  }
157 
158  for (const auto& type : config.m_types_to_check)
159  {
160  const auto it = std::find(all_checkable_candidate_types.begin(), all_checkable_candidate_types.end(), type);
161 
162  if (it == all_checkable_candidate_types.end())
163  {
164  log_error("module_identification", "no candidate generation available for type {}", enum_to_string(type));
165  continue;
166  }
167 
168  const auto start_pre_processing = std::chrono::steady_clock::now();
169  auto new_candidates_res = FunctionalCandidate::create_candidates(sc, config.m_max_control_signals, sc->ctx, type, config.m_known_registers);
170  const u64 duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_pre_processing).count();
171 
172  if (new_candidates_res.is_error())
173  {
174  return ERR(new_candidates_res.get_error().get());
175  }
176  auto new_candidates = new_candidates_res.get();
177 
178  std::map<std::string, std::map<std::string, std::map<std::string, std::map<std::string, u64>>>> new_entries = {};
179  new_entries["PRE_PROCESSING"][enum_to_string(type)]["CANDIDATE_CREATION"]["DURATION"] += duration;
180  new_entries["PRE_PROCESSING"][enum_to_string(type)]["CANDIDATE_CREATION"]["AMOUNT"] += new_candidates.size();
181  stats.add_stat(sc->base_candidate, new_entries);
182 
183  for (auto& nc : new_candidates)
184  {
185  result.push_back(nc);
186  }
187  }
188 
189  return OK(result);
190  }
191 
192  namespace
193  {
194  // TODO this is a mess of multi threading and needs cleaning up badly at some point
195  void work(std::vector<StructuralCandidate*>& structural_candidates_work_queque,
196  std::vector<std::unique_ptr<StructuralCandidate>>& structural_candidates,
197  std::vector<std::unique_ptr<StructuralCandidate>>& done_structural_candidates,
198  std::atomic<u32>& structural_workload,
199  std::deque<std::pair<std::unique_ptr<StructuralCandidate>, std::vector<FunctionalCandidate>>>& functional_candidates,
200  std::map<BaseCandidate*, std::vector<VerifiedCandidate>>& verified_candidates,
201  const Configuration& config,
202  Statistics& stats,
203  std::vector<std::string>& status,
204  std::vector<std::string>& candidate_info,
205  std::mutex& thread_sync,
206  const u32 thread_idx)
207  {
208  std::map<BaseCandidate*, std::vector<VerifiedCandidate>> verified_candidates_cache;
209 
210  const auto work_structural =
211  [&config, &structural_candidates_work_queque, &structural_candidates, &structural_workload, &functional_candidates, &stats, &status, &candidate_info, &thread_sync, &thread_idx]()
212  -> bool {
213  // check for structural OverarchingCandidate workloads
214  if (!structural_candidates_work_queque.empty())
215  {
216  // const auto sc = structural_candidates_work_queque.back();
217  structural_candidates_work_queque.pop_back();
218 
219  auto unique_sc = std::move(structural_candidates.back());
220  structural_candidates.pop_back();
221 
222  thread_sync.unlock();
223 
224  candidate_info.at(thread_idx) = "S_" + unique_sc->base_candidate->m_gates.front()->get_name();
225 
226  auto new_functional_candidates_res = generate_functional_candidates(unique_sc.get(), config, stats);
227  if (new_functional_candidates_res.is_error())
228  {
229  log_error("module_identification",
230  "failed to generate functional candidates for carry chain {}: {}",
231  unique_sc.get()->m_gates.front()->get_name(),
232  new_functional_candidates_res.get_error().get());
233  return true;
234  }
235  std::vector<FunctionalCandidate> new_functional_candidates = new_functional_candidates_res.get();
236 
237  for (const auto& fc : new_functional_candidates)
238  {
239  const auto _bfs = unique_sc.get()->ctx.get_boolean_functions(fc.m_output_nets, fc.m_control_mapping);
240  }
241 
242  structural_workload -= 1;
243  if (!new_functional_candidates.empty())
244  {
245  thread_sync.lock();
246  status.at(thread_idx) = "LOCKING STRUCTUAL at " + std::to_string(thread_idx);
247  functional_candidates.push_back({std::move(unique_sc), new_functional_candidates});
248  thread_sync.unlock();
249  }
250 
251  return true;
252  }
253 
254  return false;
255  };
256 
257  const auto work_functional = [&config, &functional_candidates, &done_structural_candidates, &verified_candidates_cache, &candidate_info, &thread_sync, &thread_idx, &stats]() -> bool {
258  // check for functional OverarchingCandidate workloads
259  if (!functional_candidates.empty())
260  {
261  auto& [sc, f_candidates] = functional_candidates.front();
262 
263  auto sc_ptr = functional_candidates.front().first.get();
264  auto bc = sc->base_candidate;
265  auto fc = f_candidates.back();
266 
267  if (f_candidates.size() == 1)
268  {
269  // if this was the last functional candidate from this structural candidate pop the pair from the deque
270  done_structural_candidates.push_back(std::move(sc));
271  sc_ptr = done_structural_candidates.back().get();
272 
273  functional_candidates.pop_front();
274  }
275  else
276  {
277  // ... otherwise just pop one candidate from the vector
278  f_candidates.pop_back();
279  }
280  thread_sync.unlock();
281 
282  candidate_info.at(thread_idx) = "F_" + fc.m_base_gates.front()->get_name();
283 
284  const auto output_functions_res = sc_ptr->ctx.get_boolean_functions_const(fc.m_output_nets, fc.m_control_mapping);
285  if (output_functions_res.is_error())
286  {
287  log_error("module_identification", "cannot check candidate: failed to get Boolean output functions before check.\n {}", output_functions_res.get_error().get());
288  }
289  const auto output_functions = output_functions_res.get();
290 
291  const auto res = fc.check(output_functions, config.m_known_registers);
292  if (res.is_error())
293  {
294  log_error("module_identification",
295  "failed to check current overaching candidate at carry chain {} of type {}:\n{}",
296  fc.m_gates.front()->get_name(),
297  enum_to_string(fc.m_candidate_type),
298  res.get_error().get());
299  return true;
300  }
301  stats.add_stat(bc, fc);
302 
303  auto vc = res.get();
304  vc.m_base_gates = bc->m_gates;
305 
306  if (vc.is_verified())
307  {
308  verified_candidates_cache[bc].push_back(vc);
309  }
310  return true;
311  }
312 
313  return false;
314  };
315 
316  status.at(thread_idx) = "INIT";
317  while (true)
318  {
319  // getting workload
320  candidate_info.at(thread_idx) = "NONE";
321  status.at(thread_idx) = "LOCKED";
322  thread_sync.lock();
323 
324  // all work is done, now return results
325  if ((structural_workload == 0) && functional_candidates.empty())
326  {
327  for (auto [bc, vc] : verified_candidates_cache)
328  {
329  verified_candidates.at(bc).insert(verified_candidates.at(bc).end(), vc.begin(), vc.end());
330  }
331  thread_sync.unlock();
332 
333  status.at(thread_idx) = "FINISHED";
334  return;
335  }
336 
337  if (config.m_multithreading_priority == MultithreadingPriority::time_priority)
338  {
339  status.at(thread_idx) = "RUNNING STRUCTURAL";
340  if (work_structural())
341  {
342  continue;
343  }
344 
345  status.at(thread_idx) = "RUNNING FUNCTIONAL";
346  if (work_functional())
347  {
348  continue;
349  }
350  }
351  else
352  {
353  status.at(thread_idx) = "RUNNING FUNCTIONAL";
354  if (work_functional())
355  {
356  continue;
357  }
358 
359  status.at(thread_idx) = "RUNNING STRUCTURAL";
360  if (work_structural())
361  {
362  continue;
363  }
364  }
365 
366  thread_sync.unlock();
367 
368  status.at(thread_idx) = "WAITING";
369  std::this_thread::sleep_for(std::chrono::milliseconds(200));
370  }
371  }
372 
374  execute_on_structural_candidates(std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>>& base_to_structural_candidates,
375  const Configuration& config)
376  {
377  Netlist* nl = config.m_netlist;
378 
379  // contains a list of verified candidates for each base candidate
380  std::map<BaseCandidate*, std::vector<VerifiedCandidate>> verified_candidates;
381 
382  std::vector<StructuralCandidate*> structural_candidates_work_queque;
383  std::vector<std::unique_ptr<StructuralCandidate>> structural_candidates;
384  std::vector<std::unique_ptr<StructuralCandidate>> done_structural_candidates;
385  std::deque<std::pair<std::unique_ptr<StructuralCandidate>, std::vector<FunctionalCandidate>>> functional_candidates;
386 
387  for (auto& [base_cand, struct_cands] : base_to_structural_candidates)
388  {
389  // initialize verified candidate list with pointer of base candidates
390  verified_candidates.insert(std::make_pair(base_cand.get(), std::vector<VerifiedCandidate>()));
391 
392  // create a vector with all base candidates
393  for (auto& sc : struct_cands)
394  {
395  structural_candidates_work_queque.push_back(sc.get());
396  structural_candidates.push_back(std::move(sc));
397  }
398  }
399 
400  std::atomic<u32> structural_workload = structural_candidates.size();
401 
402  if (structural_candidates.empty())
403  {
404  Result(nl, std::vector<std::pair<BaseCandidate, VerifiedCandidate>>());
405  }
406 
407  const u32 num_threads = std::min(config.m_max_thread_count, std::thread::hardware_concurrency() - 1);
408 
409  log_info("module_identification", "running with {} threads and {} multithreading priority", num_threads, static_cast<int>(config.m_multithreading_priority));
410 
411  auto stats = Statistics();
412 
413  // create hal GND and VCC nets as they might be needed for the operand creation
414  const auto gnd_res = NetlistModificationDecorator(*nl).create_gnd_net();
415  if (gnd_res.is_error())
416  {
417  log_error("module_identification", "failed to create GND net: {}", gnd_res.get_error().get());
418  }
419 
420  const auto vcc_res = NetlistModificationDecorator(*nl).create_vcc_net();
421  if (vcc_res.is_error())
422  {
423  log_error("module_identification", "failed to create VCC net: {}", vcc_res.get_error().get());
424  }
425 
426  // creating workloads
427  log_info("module_identification", "running checks for {} possible candidates...", structural_candidates.size());
428  std::vector<std::thread> workers;
429  std::vector<std::string> status = {num_threads + 1, "NOT STARTED"};
430  std::vector<std::string> candidate_info = {num_threads + 1, "NONE"};
431 
432  std::mutex thread_sync;
433 
434  for (u32 i = 0; i < num_threads; ++i)
435  {
436  workers.emplace_back([&, i]() {
437  return work(structural_candidates_work_queque,
438  structural_candidates,
439  done_structural_candidates,
440  structural_workload,
441  functional_candidates,
442  verified_candidates,
443  config,
444  stats,
445  status,
446  candidate_info,
447  thread_sync,
448  i);
449  });
450  }
451 
452 #ifdef PRINT_THREAD_INFO
453  while ((structural_workload != 0) || !functional_candidates.empty())
454  {
455  std::cout << "WORKLOAD: " << structural_workload << std::endl;
456 
457  std::map<std::string, u32> status_collection;
458  for (u32 i = 0; i < num_threads; i++)
459  {
460  status_collection[status.at(i)] += 1;
461  }
462 
463  std::cout << "Thread status: " << std::endl;
464  for (const auto& [sn, sc] : status_collection)
465  {
466  std::cout << "\t" << sn << ": " << sc << std::endl;
467  }
468 
469  std::map<std::string, u32> candidate_collection;
470  for (u32 i = 0; i < num_threads; i++)
471  {
472  candidate_collection[candidate_info.at(i)] += 1;
473  }
474 
475  if (candidate_collection.size() < 10)
476  {
477  std::cout << "Candidate Info: " << std::endl;
478  for (const auto& [sn, sc] : candidate_collection)
479  {
480  std::cout << "\t" << sn << ": " << sc << std::endl;
481  }
482  }
483 
484  std::this_thread::sleep_for(std::chrono::milliseconds(500));
485  }
486 #endif
487 
488  // wait for threads to finish
489  for (auto& worker : workers)
490  {
491  worker.join();
492  }
493 
494  // if (config.s_progress_indicator_function)
495  // {
496  // // TODO use string formatter to make this pretty
497  // config.s_progress_indicator_function(100, "module identification finished\ndoing postprocessing");
498  // }
499 
500  // post processing
501  std::map<std::string, u32> type_counter;
502  std::vector<std::vector<hal::Gate*>> known_registers = config.m_known_registers;
503  std::vector<std::pair<BaseCandidate, VerifiedCandidate>> result_input;
504 
505  log_info("module_identification", "Done with module identification, now doing post processing");
506 
507  const auto start_post_processing = std::chrono::steady_clock::now();
508  for (auto& [bc, vc] : verified_candidates)
509  {
510  auto post_processing_result = post_processing(vc, nl, known_registers);
511 
512  if (!post_processing_result.is_verified())
513  {
514  post_processing_result = VerifiedCandidate({}, {}, {}, {}, {}, {}, bc->m_gates, {}, {});
515  }
516 
517  result_input.push_back(std::make_pair(*bc, post_processing_result));
518  }
519  const auto duration_post_processing = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_post_processing);
520  log_info("module_identification", "Post processing took {} ms", duration_post_processing.count());
521 
522  return OK(Result(nl, result_input, stats.to_json()));
523  }
524  } // namespace
525 
527  {
528  const Netlist* nl = config.m_netlist;
529 
530  log_info("module_identification",
531  "Executing on netlist {} / {} / {} and gatelib {}",
532  nl->get_top_module()->get_name(),
533  nl->get_design_name(),
534  nl->get_device_name(),
535  nl->get_gate_library()->get_name());
536 
537  auto res = generate_structural_candidates(nl, config);
538  if (res.is_error())
539  {
540  return ERR_APPEND(res.get_error(), "cannot execute plugin: failed structural candidate generation");
541  }
542 
543  auto base_to_structural_candidates = res.get();
544  return execute_on_structural_candidates(base_to_structural_candidates, config);
545  }
546 
547  hal::Result<Result> execute_on_gates(const std::vector<Gate*>& gates, const Configuration& config)
548  {
549  if (gates.empty())
550  {
551  return ERR("cannot execute plugin: provided gates are empty");
552  }
553 
554  std::unique_ptr<BaseCandidate> bc = std::make_unique<BaseCandidate>(gates);
555 
556  std::vector<std::unique_ptr<StructuralCandidate>> structural_vector;
557  structural_vector.emplace_back(std::make_unique<StructuralCandidate>(bc.get(), gates));
558  std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> base_to_structural_candidates;
559  base_to_structural_candidates.emplace_back(std::make_pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>(std::move(bc), std::move(structural_vector)));
560 
561  return execute_on_structural_candidates(base_to_structural_candidates, config);
562  }
563  } // namespace module_identification
564 } // namespace hal
This file contains the definition of the BaseCandidate class, which represents a base candidate in th...
This file contains the enumeration and constants for the candidate types used in the module identific...
std::string get_name() const
std::string get_name() const
Definition: module.cpp:87
Module * get_top_module() const
Definition: netlist.cpp:608
const std::string & get_device_name() const
Definition: netlist.cpp:118
const std::string & get_design_name() const
Definition: netlist.cpp:104
const GateLibrary * get_gate_library() const
Definition: netlist.cpp:132
Result< std::set< const Net * > > get_subgraph_function_inputs(const std::vector< const Gate * > &subgraph_gates, const Net *subgraph_output) const
static hal::Result< std::vector< FunctionalCandidate > > create_candidates(StructuralCandidate *sc, u32 max_control_signal, CandidateContext &ctx, module_identification::CandidateType candidate_type, const std::vector< std::vector< Gate * >> &registers)
Create functional candidates from a structural candidate.
A class representing a structural candidate for module identification.
BaseCandidate * base_candidate
Pointer to the base candidate.
std::vector< Gate * > m_gates
Vector of gates that form the structural candidate.
CandidateContext ctx
Candidate context for the structural candidate.
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
This file contains the class and functions for handling functional candidates within the module ident...
#define log_error(channel,...)
Definition: log.h:78
#define log_info(channel,...)
Definition: log.h:70
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
This file contains the function to generate structural candidates for Lattice iCE40 FPGAs.
This file contains the struct Configuration for module identification analysis.
This file contains the function declarations for the Module Identification plugin in hal.
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.
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.
hal::Result< std::vector< FunctionalCandidate > > generate_functional_candidates(StructuralCandidate *sc, const Configuration &config, Statistics &stats)
const std::vector< CandidateType > all_checkable_candidate_types
A list of all candidate types that are selectable to be checked.
hal::Result< std::vector< std::pair< std::unique_ptr< BaseCandidate >, std::vector< std::unique_ptr< StructuralCandidate > > > > > generate_structural_candidates(const Netlist *nl, const Configuration &config)
hal::Result< Result > execute(const Configuration &config)
Perform a full run of the module identification process on the given netlist with the provided config...
VerifiedCandidate post_processing(const std::vector< VerifiedCandidate > &verified_candidates, const Netlist *nl, const std::vector< std::vector< Gate * >> &dana_cache)
Performs post-processing on a set of verified candidates to identify the best candidate for module id...
hal::Result< Result > execute_on_gates(const std::vector< Gate * > &gates, const Configuration &config)
Perform a module identification run on the specified gates with the provided configuration.
std::vector< Net * > get_output_nets(const std::vector< Gate * > &gates, bool only_external_destinations=true)
Get output nets from a list of gates.
Definition: utils.cpp:61
@ time_priority
Prioritize time efficiency in multithreading.
Definition: defines.h:45
std::string enum_to_string(T e)
Definition: enums.h:53
PinType type
This file contains the structures and functions related to module identification results.
This file contains helper functions for module identification in the HAL framework.
This file contains the function to perform post-processing on verified candidates to identify the bes...
std::vector< Gate * > m_gates
The gates of the corresponding structural candidate.
hal::Result< std::monostate > populate_boolean_function_cache(const std::vector< Net * > nets)
Populates the boolean function cache for a set of nets.
Configuration for the module identification analysis.
Definition: configuration.h:55
Netlist * m_netlist
The netlist to be analyzed.
Definition: configuration.h:71
std::vector< std::set< Gate * > > m_blocked_base_candidates
Base candidates to block during analysis.
std::vector< module_identification::CandidateType > m_types_to_check
CandidateTypes that shall be checked. Defaults to all checkable candidate types.
Definition: configuration.h:81
std::vector< std::vector< Gate * > > m_known_registers
A vector handling possibly known registers.
Definition: configuration.h:76
u32 m_max_control_signals
Maximum number of control signals to be tested. Defaults to 3.
Definition: configuration.h:91
std::vector< std::vector< Gate * > > m_already_classified_candidates
Gates to ignore during processing.
Definition: configuration.h:96
void add_stat(const BaseCandidate *bc, const FunctionalCandidate &oc)
Definition: statistics.h:28
This file contains the class for defining and managing structural candidates within the module identi...
This file contains the function to generate structural candidates for Xilinx Unisim libraries.