HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
post_processing.cpp
Go to the documentation of this file.
2 
3 #include "hal_core/defines.h"
6 #include "hal_core/netlist/net.h"
12 
13 #include <deque>
14 #include <limits>
15 #include <numeric>
16 
17 // #define DEBUG_PRINT
18 
19 namespace hal
20 {
21  namespace module_identification
22  {
23  namespace
24  {
25  std::vector<std::vector<VerifiedCandidate>> filter_out_redundant_const_muls(std::vector<std::vector<VerifiedCandidate>>& candidate_sets)
26  {
27  std::set<std::vector<Net*>> found_counter_for_output;
28  for (const auto& cs : candidate_sets)
29  {
30  if (std::find(cs.front().m_types.begin(), cs.front().m_types.end(), CandidateType::counter) != cs.front().m_types.end())
31  {
32  found_counter_for_output.insert(cs.front().m_output_nets);
33  }
34  }
35 
36  std::vector<std::vector<VerifiedCandidate>> filtered_candidates;
37  for (const auto& cs : candidate_sets)
38  {
39  if (std::find(cs.front().m_types.begin(), cs.front().m_types.end(), CandidateType::constant_multiplication) != cs.front().m_types.end()
40  || std::find(cs.front().m_types.begin(), cs.front().m_types.end(), CandidateType::constant_multiplication_offset) != cs.front().m_types.end())
41  {
42  if (found_counter_for_output.find(cs.front().m_output_nets) != found_counter_for_output.end())
43  {
44  continue;
45  }
46  }
47 
48  filtered_candidates.push_back(cs);
49  }
50 
51 #ifdef DEBUG_PRINT
52  std::cout << "Left with the following sets: " << std::endl;
53  for (const auto& cs : filtered_candidates)
54  {
55  std::cout << enum_to_string(*cs.front().m_types.begin()) << ": " << cs.size() << std::endl;
56  for (const auto& o_net : cs.front().m_output_nets)
57  {
58  std::cout << "\t" << o_net->get_name() << std::endl;
59  }
60  }
61 #endif
62 
63  return filtered_candidates;
64  }
65 
66  // calculates how many registers both feed into the operands of a candidate and into the control signals
67  u32 calculate_shared_source_regs(const Netlist* nl, const std::vector<VerifiedCandidate>& candidates, const std::vector<std::vector<Gate*>>& registers)
68  {
69  u32 shared_input_regs = 0;
70  for (const auto& c : candidates)
71  {
72  std::set<u32> op_regs;
73  for (const auto& nets : c.m_operands)
74  {
75  // TODO make this use the util find_neighboring_registers() function
76  for (const auto& n : nets)
77  {
78  const auto seq_inputs_res =
79  NetlistTraversalDecorator(*nl).get_next_matching_gates(n, false, [](const auto& g) { return g->get_type()->has_property(GateTypeProperty::sequential); });
80  if (seq_inputs_res.is_error())
81  {
82  log_error("module_identification", "{}", seq_inputs_res.get_error().get());
83  }
84  const auto seq_inputs = seq_inputs_res.get();
85 
86  for (const auto& si : seq_inputs)
87  {
88  for (u32 reg_idx = 0; reg_idx < registers.size(); reg_idx++)
89  {
90  const auto& reg = registers.at(reg_idx);
91  if (std::find(reg.begin(), reg.end(), si) != reg.end())
92  {
93  op_regs.insert(reg_idx);
94  break;
95  }
96  }
97  }
98  }
99  }
100 
101  // TODO make this use the util find_neighboring_registers() function
102  std::set<u32> ctrl_regs;
103  for (const auto& n : c.m_control_signals)
104  {
105  const auto seq_inputs_res =
106  NetlistTraversalDecorator(*nl).get_next_matching_gates(/*cache, */ n, false, [](const auto& g) { return g->get_type()->has_property(GateTypeProperty::sequential); });
107  if (seq_inputs_res.is_error())
108  {
109  log_error("module_identification", "{}", seq_inputs_res.get_error().get());
110  }
111  const auto seq_inputs = seq_inputs_res.get();
112 
113  for (const auto& si : seq_inputs)
114  {
115  for (u32 reg_idx = 0; reg_idx < registers.size(); reg_idx++)
116  {
117  const auto& reg = registers.at(reg_idx);
118  if (std::find(reg.begin(), reg.end(), si) != reg.end())
119  {
120  ctrl_regs.insert(reg_idx);
121  break;
122  }
123  }
124  }
125  }
126 
127  std::vector<u32> intersection;
128  std::set_intersection(op_regs.begin(), op_regs.end(), ctrl_regs.begin(), ctrl_regs.end(), std::back_inserter(intersection));
129 
130  shared_input_regs += intersection.size();
131  }
132 
133  return shared_input_regs;
134  }
135 
136  // calculates the maximum amount of inputs not covered by the candidate operands or control signals
137  u32 calcualte_ignored_input_signals(const std::vector<VerifiedCandidate>& candidate_set)
138  {
139  u32 max_inputs_ignored = 0;
140  std::set<Net*> covered_nets;
141  for (const auto& c : candidate_set)
142  {
143  // first collect all operand nets
144  for (const auto& op : c.m_operands)
145  {
146  for (const auto& net : op)
147  {
148  if (net->is_gnd_net() || net->is_vcc_net())
149  {
150  continue;
151  }
152  covered_nets.insert(net);
153  }
154  }
155 
156  // we treat control signals as covered
157  covered_nets.insert(c.m_control_signals.begin(), c.m_control_signals.end());
158  }
159 
160  for (const auto& c : candidate_set)
161  {
162  // second collect all nets that are not part of the operands
163  u32 ignored_inputs = 0;
164  const auto all_input_nets = get_input_nets(c.m_gates);
165  for (const auto& input_net : all_input_nets)
166  {
167  if (input_net->is_gnd_net() || input_net->is_vcc_net())
168  {
169  continue;
170  }
171 
172  if (covered_nets.find(input_net) == covered_nets.end())
173  {
174  ignored_inputs++;
175  }
176  }
177 
178  max_inputs_ignored = std::max(max_inputs_ignored, ignored_inputs);
179  }
180 
181  return max_inputs_ignored;
182  }
183 
184  // calculates the maximum amount of nets belonging to the input operands and the outputs
185  u32 calculate_maximum_io_signals(const std::vector<VerifiedCandidate>& candidate_set)
186  {
187  u32 max_io_signals = 0;
188  for (const auto& c : candidate_set)
189  {
190  // first collect all operand nets
191  std::set<Net*> io_signals;
192  for (const auto& op : c.m_operands)
193  {
194  for (const auto& net : op)
195  {
196  if (net->is_gnd_net() || net->is_vcc_net())
197  {
198  continue;
199  }
200  io_signals.insert(net);
201  }
202  }
203 
204  io_signals.insert(c.m_output_nets.begin(), c.m_output_nets.end());
205 
206  max_io_signals = std::max(max_io_signals, (u32)io_signals.size());
207  }
208 
209  return max_io_signals;
210  }
211 
212  // calculates the maximum amount of outputs not covered by the candidate output
213  u32 calcualte_ignored_output_signals(const std::vector<VerifiedCandidate>& candidate_set)
214  {
215  u32 max_outputs_ignored = 0;
216  for (const auto& c : candidate_set)
217  {
218  const auto all_outputs = get_output_nets(c.m_gates, false).size();
219  const auto c_outputs = c.m_output_nets.size();
220  const auto outputs_ignored = (c_outputs > all_outputs) ? 0 : all_outputs - c_outputs;
221 
222  if (outputs_ignored > max_outputs_ignored)
223  {
224  max_outputs_ignored = outputs_ignored;
225  }
226  }
227 
228  return max_outputs_ignored;
229  }
230 
231  // filters a set of candidates by a provided criteria and returns only the sets performing the best
232  std::vector<std::vector<VerifiedCandidate>> filter_sets_by(const std::vector<std::vector<VerifiedCandidate>>& candidate_sets,
233  const std::vector<std::vector<Gate*>>& registers,
234  const bool minimize,
235  const std::function<u32(const std::vector<VerifiedCandidate>&, const std::vector<std::vector<Gate*>>& registers)>& criteria)
236  {
237  std::vector<u32> fitlered_indices;
238  u32 best_criteria_value = minimize ? std::numeric_limits<u32>::max() : std::numeric_limits<u32>::min();
239  for (u32 idx = 0; idx < candidate_sets.size(); idx++)
240  {
241  const u32 criteria_val = criteria(candidate_sets.at(idx), registers);
242  const bool new_best = minimize ? (criteria_val < best_criteria_value) : (criteria_val > best_criteria_value);
243  const bool equal = (criteria_val == best_criteria_value);
244 
245  if (new_best)
246  {
247  best_criteria_value = criteria_val;
248  fitlered_indices.clear();
249  fitlered_indices.push_back(idx);
250  continue;
251  }
252 
253  if (equal)
254  {
255  fitlered_indices.push_back(idx);
256  }
257  }
258 
259  std::vector<std::vector<VerifiedCandidate>> filtered_candidates;
260  for (const auto& idx : fitlered_indices)
261  {
262  filtered_candidates.push_back(candidate_sets.at(idx));
263  }
264 
265  return filtered_candidates;
266  }
267 
268  std::vector<VerifiedCandidate> select_best_candidate_set(const Netlist* nl, const std::vector<VerifiedCandidate>& candidates, const std::vector<std::vector<Gate*>>& registers)
269  {
270  // dedupe candidates, dont know how this happens
271  std::vector<VerifiedCandidate> unique_candidates = candidates;
272  unique_candidates.erase(std::unique(unique_candidates.begin(), unique_candidates.end()), unique_candidates.end());
273 
274 #ifdef DEBUG_PRINT
275  std::cout << "Dedupe candidates to a size of " << unique_candidates.size() << std::endl;
276 #endif
277 
278  // sort the candidates into sets of candidates with the same gates and control signals
279  std::map<std::pair<std::vector<Gate*>, std::vector<Net*>>, std::vector<VerifiedCandidate>> gates_control_to_candidate_sets;
280  for (const auto& c : unique_candidates)
281  {
282  gates_control_to_candidate_sets[{c.m_gates, c.m_control_signals}].push_back(c);
283  }
284 
285  std::vector<std::vector<VerifiedCandidate>> candidate_sets;
286  for (const auto& [_, c_set] : gates_control_to_candidate_sets)
287  {
288  candidate_sets.push_back(c_set);
289  }
290 
291 #ifdef DEBUG_PRINT
292  std::cout << "Sorted candidates into " << candidate_sets.size() << " candidate sets" << std::endl;
293 #endif
294 
299  candidate_sets = filter_out_redundant_const_muls(candidate_sets);
300 #ifdef DEBUG_PRINT
301  std::cout << "Filtered redundant constant multiplication candidates, left with " << candidate_sets.size() << std::endl;
302 #endif
303 
304  // filter the candidate sets such that only the ones with the least input signals not part of the operands survive
305  candidate_sets = filter_sets_by(candidate_sets, registers, true, [](const auto& c_set, const auto& _registers) {
306  UNUSED(_registers);
307  return calcualte_ignored_input_signals(c_set);
308  });
309 #ifdef DEBUG_PRINT
310  std::cout << "Left with the following sets: " << std::endl;
311  for (const auto& cs : candidate_sets)
312  {
313  std::cout << enum_to_string(*cs.front().m_types.begin()) << ": " << cs.size() << std::endl;
314  for (const auto& o_net : cs.front().m_output_nets)
315  {
316  std::cout << "\t" << o_net->get_name() << std::endl;
317  }
318  }
319  std::cout << "Filtered candidates with ignored input signals, left with " << candidate_sets.size() << std::endl;
320 #endif
321 
322  // filter the candidate sets such that only the ones with the most input and output signals survive
323  candidate_sets = filter_sets_by(candidate_sets, registers, false, [](const auto& c_set, const auto& _registers) {
324  UNUSED(_registers);
325  return calculate_maximum_io_signals(c_set);
326  });
327 #ifdef DEBUG_PRINT
328  std::cout << "Filtered candidates with most IO signals, left with " << candidate_sets.size() << std::endl;
329 #endif
330 
331  // filter the candidate set such that only the ones with the least control signals survive
332  candidate_sets = filter_sets_by(candidate_sets, registers, true, [](const auto& c_set, const auto& _registers) {
333  UNUSED(_registers);
334  return c_set.front().m_control_signals.size();
335  });
336 #ifdef DEBUG_PRINT
337  std::cout << "Filtered candidates with control signal count, left with " << candidate_sets.size() << std::endl;
338 #endif
339 
340  // filter the candidate sets such that only the ones with the most gates survive
341  candidate_sets = filter_sets_by(candidate_sets, registers, false, [](const auto& c_set, const auto& _registers) {
342  UNUSED(_registers);
343  return c_set.front().m_gates.size();
344  });
345 #ifdef DEBUG_PRINT
346  std::cout << "Filtered candidates with candidate gate count, left with " << candidate_sets.size() << std::endl;
347 #endif
348 
349  // select candidate sets with the control signals that share the least source flip flops with the operands bits in the DANA grouping
350  candidate_sets = filter_sets_by(candidate_sets, registers, true, [nl](const auto& c_set, const auto& r) { return calculate_shared_source_regs(nl, c_set, r); });
351 #ifdef DEBUG_PRINT
352  std::cout << "Filtered candidates with shared source reg count, left with " << candidate_sets.size() << std::endl;
353 #endif
354 
355  // select the candidate set with the least amount of "module outputs" that are not part of the output bitvector
356  candidate_sets = filter_sets_by(candidate_sets, registers, true, [](const auto& c_set, const auto& _registers) {
357  UNUSED(_registers);
358  return calcualte_ignored_output_signals(c_set);
359  });
360 #ifdef DEBUG_PRINT
361  std::cout << "Filtered candidates with ignored output signal count, left with " << candidate_sets.size() << std::endl;
362 #endif
363 
364  // select the candidate set with the most candidates
365  candidate_sets = filter_sets_by(candidate_sets, registers, false, [](const auto& c_set, const auto& _registers) {
366  UNUSED(_registers);
367  return c_set.size();
368  });
369 #ifdef DEBUG_PRINT
370  std::cout << "Reduced candidates to " << candidate_sets.size() << " sets." << std::endl;
371 #endif
372 
373  // TODO remove debug printing
374  if (candidate_sets.size() > 1)
375  {
376  log_warning("module_identification", "Found {} optimal candidate sets, need to investigate furhter.", candidate_sets.size());
377  }
378 
379  return candidate_sets.front();
380  }
381  } // namespace
382 
383  namespace
384  {
385  // calculates the sum of all registers that feed into any candidate operand
386  u32 calculate_operand_source_regs(const Netlist* nl, const VerifiedCandidate& candidate, const std::vector<std::vector<Gate*>>& registers)
387  {
388  u32 total_source_regs = 0;
389  for (const auto& nets : candidate.m_operands)
390  {
391  std::set<u32> op_regs;
392  for (const auto& n : nets)
393  {
394  const auto seq_inputs_res =
395  NetlistTraversalDecorator(*nl).get_next_matching_gates(/*cache, */ n, false, [](const auto& g) { return g->get_type()->has_property(GateTypeProperty::sequential); });
396  if (seq_inputs_res.is_error())
397  {
398  log_error("module_identification", "{}", seq_inputs_res.get_error().get());
399  }
400  const auto seq_inputs = seq_inputs_res.get();
401 
402  for (const auto& si : seq_inputs)
403  {
404  for (u32 reg_idx = 0; reg_idx < registers.size(); reg_idx++)
405  {
406  const auto& reg = registers.at(reg_idx);
407  if (std::find(reg.begin(), reg.end(), si) != reg.end())
408  {
409  op_regs.insert(reg_idx);
410  break;
411  }
412  }
413  }
414  }
415 
416  total_source_regs += op_regs.size();
417  }
418 
419  return total_source_regs;
420  }
421 
422  VerifiedCandidate select_best_sign_extended_comparison_candidate(const Netlist* nl, const std::vector<VerifiedCandidate>& candidates)
423  {
424  std::set<Net*> unique_nets;
425  for (const auto& op_nets : candidates.front().m_operands)
426  {
427  for (const auto& net : op_nets)
428  {
429  if (net->is_gnd_net() || net->is_vcc_net())
430  {
431  continue;
432  }
433 
434  unique_nets.insert(net);
435  }
436  }
437 
438  // find the set of gates that is reachable by more than one net of the operands nets
439  const std::set<const Gate*> candidate_gates = {candidates.front().m_gates.begin(), candidates.front().m_gates.end()};
440 
441  std::map<Net*, std::set<Gate*>> net_to_reachable_gates;
442  std::map<Gate*, u32> gate_to_reach_counter;
443 
444  for (const auto& net : unique_nets)
445  {
446  const auto reachable_res =
447  NetlistTraversalDecorator(*nl).get_next_matching_gates_until(net, true, [&candidate_gates](const auto& g) { return candidate_gates.find(g) != candidate_gates.end(); });
448 
449  net_to_reachable_gates.insert({net, reachable_res.get()});
450 
451  for (const auto& gate : reachable_res.get())
452  {
453  gate_to_reach_counter[gate] += 1;
454  }
455  }
456 
457  // for furhter analysis we do not consider all gates of the candidate, but only the gates that are reached by at least two other nets
458  std::set<Gate*> core_gates;
459  for (const auto& [gate, count] : gate_to_reach_counter)
460  {
461  if (count > 1)
462  {
463  core_gates.insert(gate);
464  }
465  }
466 
467  u32 best_candidate_idx = 0;
468  u32 most_reached_gates = 0;
469 
470  for (u32 idx = 0; idx < candidates.size(); idx++)
471  {
472  const auto& candidate = candidates.at(idx);
473 
474  // find the sign extended bit for each candidate
475  Net* signed_net = nullptr;
476  for (const auto& operand : candidate.m_operands)
477  {
478  if (operand[operand.size() - 1] == operand[operand.size() - 2])
479  {
480  signed_net = operand[operand.size() - 1];
481  break;
482  }
483  }
484  if (signed_net == nullptr)
485  {
486  continue;
487  }
488 
489  // select the candidate whose extended sign bit leads to most of the core gates
490  std::vector<Gate*> reachable_core_gates;
491  std::set_intersection(
492  net_to_reachable_gates.at(signed_net).begin(), net_to_reachable_gates.at(signed_net).end(), core_gates.begin(), core_gates.end(), std::back_inserter(reachable_core_gates));
493 
494  if (reachable_core_gates.size() > most_reached_gates)
495  {
496  most_reached_gates = reachable_core_gates.size();
497  best_candidate_idx = idx;
498  }
499  }
500 
501  return candidates.at(best_candidate_idx);
502  }
503 
504  std::vector<VerifiedCandidate> filter_candidates_by(const std::vector<VerifiedCandidate>& candidates,
505  const std::vector<std::vector<Gate*>>& registers,
506  const bool minimize,
507  const std::function<u32(const VerifiedCandidate&, const std::vector<std::vector<Gate*>>& registers)>& criteria)
508  {
509  std::vector<u32> fitlered_indices;
510  u32 best_criteria_value = minimize ? std::numeric_limits<u32>::max() : std::numeric_limits<u32>::min();
511  for (u32 idx = 0; idx < candidates.size(); idx++)
512  {
513  const u32 criteria_val = criteria(candidates.at(idx), registers);
514  const bool new_best = minimize ? (criteria_val < best_criteria_value) : (criteria_val > best_criteria_value);
515  const bool equal = (criteria_val == best_criteria_value);
516 
517  if (new_best)
518  {
519  best_criteria_value = criteria_val;
520  fitlered_indices.clear();
521  fitlered_indices.push_back(idx);
522  continue;
523  }
524 
525  if (equal)
526  {
527  fitlered_indices.push_back(idx);
528  }
529  }
530 
531  std::vector<VerifiedCandidate> filtered_candidates;
532  for (const auto& idx : fitlered_indices)
533  {
534  filtered_candidates.push_back(candidates.at(idx));
535  }
536 
537  return filtered_candidates;
538  }
539 
540  std::vector<VerifiedCandidate> select_best_candidates(const Netlist* nl, const std::vector<VerifiedCandidate>& candidates, const std::vector<std::vector<Gate*>>& registers)
541  {
542  auto best_candidates = candidates;
543 
547  best_candidates = filter_candidates_by(best_candidates, registers, true, [](const auto& c, const auto& _r) {
548  UNUSED(_r);
549  const auto all_outputs = get_output_nets(c.m_gates, false).size();
550  const auto c_outputs = c.m_output_nets.size();
551  const auto additional_outputs = (c_outputs > all_outputs) ? 0 : all_outputs - c_outputs;
552  return additional_outputs;
553  });
554 
555 #ifdef DEBUG_PRINT
556  std::cout << "reduced candidate set to size " << best_candidates.size() << std::endl;
557 #endif
558 
562  best_candidates = filter_candidates_by(best_candidates, registers, false, [](const auto& c, const auto& _r) {
563  UNUSED(_r);
564  u32 non_constant_count = 0;
565  for (const auto& nets : c.m_operands)
566  {
567  for (const auto& net : nets)
568  {
569  non_constant_count = (!net->is_gnd_net() && !net->is_vcc_net()) ? non_constant_count + 1 : non_constant_count;
570  }
571  }
572  return non_constant_count;
573  });
574 
575 #ifdef DEBUG_PRINT
576  std::cout << "reduced candidate set to size " << best_candidates.size() << std::endl;
577 #endif
578 
584  best_candidates = filter_candidates_by(best_candidates, registers, true, [](const auto& c, const auto& _r) {
585  UNUSED(_r);
586  u32 constant_count = 0;
587  for (const auto& nets : c.m_operands)
588  {
589  for (const auto& net : nets)
590  {
591  constant_count = (!net->is_gnd_net() && !net->is_vcc_net()) ? constant_count : constant_count + 1;
592  }
593  }
594  return constant_count;
595  });
596 
597 #ifdef DEBUG_PRINT
598  std::cout << "reduced candidate set to size " << best_candidates.size() << std::endl;
599 #endif
600 
606  best_candidates = filter_candidates_by(best_candidates, registers, true, [](const auto& c, const auto& _r) {
607  UNUSED(_r);
608  u32 multi_count = 0;
609  std::map<Net*, std::set<u32>> net_to_ops;
610  for (u32 op_idx = 0; op_idx < c.m_operands.size(); op_idx++)
611  {
612  const auto& nets = c.m_operands.at(op_idx);
613  for (const auto& net : nets)
614  {
615  net_to_ops[net].insert(op_idx);
616  }
617  }
618 
619  for (const auto& [_, ops] : net_to_ops)
620  {
621  if (ops.size() > 1)
622  {
623  multi_count++;
624  }
625  }
626  return multi_count;
627  });
628 
629 #ifdef DEBUG_PRINT
630  std::cout << "reduced candidate set to size " << best_candidates.size() << std::endl;
631 #endif
632 
638  best_candidates = filter_candidates_by(best_candidates, registers, true, [nl](const auto& c, const auto& r) { return calculate_operand_source_regs(nl, c, r); });
639 
640 #ifdef DEBUG_PRINT
641  std::cout << "reduced candidate set to size " << best_candidates.size() << std::endl;
642 #endif
643 
648  std::vector<VerifiedCandidate> reducable_signed_comparisons;
649  std::vector<std::vector<VerifiedCandidate>::iterator> to_remove;
650  for (auto it = best_candidates.begin(); it != best_candidates.end(); it++)
651  {
652  if (*(it->m_types.begin()) == CandidateType::signed_less_equal || *(it->m_types.begin()) == CandidateType::signed_less_than)
653  {
654  reducable_signed_comparisons.push_back((*it));
655  to_remove.push_back(it);
656  }
657  }
658  for (const auto& elem : to_remove)
659  {
660  best_candidates.erase(elem);
661  }
662  if (reducable_signed_comparisons.size() > 0)
663  {
664  best_candidates.push_back(select_best_sign_extended_comparison_candidate(nl, reducable_signed_comparisons));
665  }
666 
667  return best_candidates;
668  }
669  } // namespace
670 
671  namespace
672  {
673  /*
674  * This function tries to find a offset between to origins with the help of a previously generated offset matrix.
675  * That matrix stores every known offset between two origins.
676  * By building a chain of known origin-offset pairs we try to find offsets even for origins that do not share an already known offset.
677  * During the chain building we populate the matrix along the way incase we find a valid offset.
678  */
679  Result<i32> get_offset(const u32& org1, const u32& org2, std::map<u32, std::map<u32, i32>>& m, std::set<std::set<u32>>& v)
680  {
681  if (v.find({org1, org2}) != v.end())
682  {
683  return ERR("Already tried to follow that offset.");
684  }
685 
686  v.insert({org1, org2});
687 
688  if (org1 == org2)
689  {
690  m[org1][org2] = 0;
691  return OK(0);
692  }
693 
694  if (m.find(org1) == m.end())
695  {
696  return ERR("No valid offset to other origins.");
697  }
698 
699  if (m.at(org1).find(org2) != m.at(org1).end())
700  {
701  return OK(m.at(org1).at(org2));
702  }
703 
704  for (auto& [dst, first_proxy_offset] : m.at(org1))
705  {
706  auto second_proxy_offset_res = get_offset(dst, org2, m, v);
707  if (second_proxy_offset_res.is_error())
708  {
709  continue;
710  }
711  i32 second_proxy_offset = second_proxy_offset_res.get();
712 
713  m[org1][org2] = first_proxy_offset + second_proxy_offset;
714  return OK(first_proxy_offset + second_proxy_offset);
715  }
716 
717  return ERR("Not able to find a offset connection.");
718  }
719 
720  /*
721  * This function tries to build an offset matrix that maps each operand origin to all the other operand origins that overlap by providing an index for the same net.
722  * Since that index maybe different we calulate an offset and check whether that offset is the same for all nets where the two origins overlap.
723  * The matrix is populated in a way that the offsetat matrix[org_0][org_1] allow the user to calculate the index_1 = index_0 + offset.
724  */
725  Result<std::map<u32, std::map<u32, i32>>> build_offset_matrix(const std::map<Net*, std::map<u32, u32>>& indices)
726  {
727  // offset at matrix[org_0][org_1] means index_0 + offset = index_1
728  std::map<u32, std::map<u32, i32>> origin_offset_matrix;
729 
730  for (const auto& [net, org_to_idx] : indices)
731  {
732  std::map<u32, u32> all_possible_indices;
733 
734  // fill all possible indices
735  for (const auto& [org, idx] : org_to_idx)
736  {
737  all_possible_indices[org] = idx;
738  }
739 
740  // check whether all possible indices are just shifted version of each other with a stable offset
741  for (const auto& [org, idx] : org_to_idx)
742  {
743  for (const auto& [already_set_org, already_set_index] : all_possible_indices)
744  {
745  // there does not yet exist an offset between the already set index and the one to be added next
746  if (origin_offset_matrix[org].find(already_set_org) == origin_offset_matrix[org].end())
747  {
748  i32 new_index = idx;
749  i32 offset = already_set_index - new_index;
750 
751  origin_offset_matrix[org][already_set_org] = offset;
752  origin_offset_matrix[already_set_org][org] = -offset;
753  }
754  // check wether the already existing offset leads to the same index
755  else
756  {
757  i32 new_index = idx;
758  i32 offset = origin_offset_matrix.at(org).at(already_set_org);
759 
760  if (new_index + offset != i32(already_set_index))
761  {
762  return ERR("unable to build offset matrix: failed to find valid offset for net " + net->get_name() + " with ID " + std::to_string(net->get_id()) + " between "
763  + std::to_string(org) + " and " + std::to_string(already_set_org));
764  }
765  }
766  }
767  }
768  }
769 
770  return OK(origin_offset_matrix);
771  }
772 
776  std::vector<Net*> reconstruct_shifted_operand(const std::vector<std::vector<Net*>>& operands)
777  {
778  std::map<Net*, std::map<u32, u32>> indices;
779  for (u32 op_idx = 0; op_idx < operands.size(); op_idx++)
780  {
781  const auto& operand = operands.at(op_idx);
782  std::set<Net*> included_nets;
783 
784  bool found_non_const_net = false;
785  for (u32 net_idx = 0; net_idx < operand.size(); net_idx++)
786  {
787  const auto& net = operand.at(net_idx);
788 
789  // TODO think about whether this actually works or we cannot just ignore all gnd and power nets all together
790  if (!net->is_gnd_net() && !net->is_vcc_net())
791  {
792  found_non_const_net = true;
793  }
794 
795  // only include the first index of each net of the operand
796  if (included_nets.find(net) != included_nets.end())
797  {
798  continue;
799  }
800 
801  // do not include constant extensions (skip constant nets that come after non const nets)
802  if ((net->is_gnd_net() || net->is_vcc_net()) && found_non_const_net)
803  {
804  continue;
805  }
806 
807  included_nets.insert(net);
808 
809  indices[net].insert({op_idx, net_idx});
810  }
811  }
812 
813  // try to find a consens between the different possible indices
814  std::map<Net*, i32> consens_bitindices;
815 
816  auto offset_matrix_res = build_offset_matrix(indices);
817  if (offset_matrix_res.is_error())
818  {
819  log_error("module_identification", "failed to build offset matrix:\n{}", offset_matrix_res.get_error().get());
820  return {};
821  }
822  auto offset_matrix = offset_matrix_res.get();
823 
824  auto base_line = offset_matrix.begin()->first;
825 
826  for (const auto& [net, possible_indices] : indices)
827  {
828  // pair of first possible org_mod and org_pin_group
829  u32 org = possible_indices.begin()->first;
830  // index at first possible origin
831  i32 org_index = possible_indices.begin()->second;
832  std::set<std::set<u32>> v;
833  auto offset_res = get_offset(org, base_line, offset_matrix, v);
834  if (offset_res.is_error())
835  {
836  log_error("module_identification",
837  "failed to reconstruct shifted operand: failed to find offset for net " + net->get_name() + " with ID " + std::to_string(net->get_id()) + " bewteen "
838  + std::to_string(org) + " and " + std::to_string(base_line));
839  return {};
840  }
841  else
842  {
843  i32 offset = offset_res.get();
844  consens_bitindices[net] = org_index + offset;
845  //std::cout << "Org Index: " << org_index << " Offset: " << offset << std::endl;
846  }
847  }
848 
849  std::vector<Net*> ordered_nets;
850  for (const auto& [net, _] : consens_bitindices)
851  {
852  if (net->is_gnd_net() || net->is_vcc_net())
853  {
854  continue;
855  }
856 
857  ordered_nets.push_back(net);
858  }
859 
860  std::sort(ordered_nets.begin(), ordered_nets.end(), [&consens_bitindices](const auto& n1, const auto& n2) { return consens_bitindices.at(n1) < consens_bitindices.at(n2); });
861 
862  return ordered_nets;
863  }
864 
865  Result<std::monostate> reconstruct_operands(VerifiedCandidate& vc, const std::vector<std::vector<Gate*>>& registers)
866  {
867  UNUSED(registers);
868  std::vector<std::vector<Net*>> operands = vc.m_operands;
869  std::vector<std::vector<Net*>> reconstructed_operands;
870 
871  if (vc.m_types.size() != 1)
872  {
873  // return ERR("cannot reconstruct operands for candidate with multiple types");
874  return OK({});
875  }
876  const auto candidate_type = *(vc.m_types.begin());
877 
879  {
880  reconstructed_operands = {reconstruct_shifted_operand(operands)};
881  vc.m_operands = reconstructed_operands;
882  }
883 
884  return OK({});
885  }
886 
887  Result<VerifiedCandidate> merge_candidate_set(const Netlist* nl, const std::vector<VerifiedCandidate>& candidate_set, const std::vector<std::vector<Gate*>>& registers)
888  {
889  std::map<std::map<Net*, BooleanFunction::Value>, std::vector<VerifiedCandidate>> ctrl_to_candidates;
890  std::vector<VerifiedCandidate> filtered_candidates;
891 
892  // merge candidates that share a control mapping
893  for (const auto& vc : candidate_set)
894  {
895  // if candidate has more than one control mapping it is already merged
896  if (vc.m_control_signal_mappings.size() == 1)
897  {
898  ctrl_to_candidates[vc.m_control_signal_mappings.front()].push_back(vc);
899  }
900  else
901  {
902  filtered_candidates.push_back(vc);
903  }
904  }
905 
906  for (const auto& [_, c] : ctrl_to_candidates)
907  {
908  if (c.size() > 1)
909  {
910  // if we find more than one candidate for a given control mapping we have to decide on one of them
911  auto best_candidate = select_best_candidates(nl, c, registers).front();
912 
913  const auto res = reconstruct_operands(best_candidate, registers);
914  if (res.is_error())
915  {
916  return ERR_APPEND(res.get_error(), "cannot merge canidate set: failed operand reconstruction");
917  }
918 
919  filtered_candidates.push_back(best_candidate);
920  }
921  else
922  {
923  auto candidate = c.front();
924  const auto res = reconstruct_operands(candidate, registers);
925  if (res.is_error())
926  {
927  return ERR_APPEND(res.get_error(), "cannot merge canidate set: failed operand reconstruction");
928  }
929 
930  filtered_candidates.push_back(candidate);
931  }
932  }
933 
934 #ifdef DEBUG_PRINT
935  std::cout << "OPTIMAL CANDIDATE SET [" << filtered_candidates.size() << "]: " << std::endl;
936  for (const auto& c : filtered_candidates)
937  {
938  std::cout << c.get_candidate_info() << std::endl;
939  }
940 #endif
941 
942  auto merge_res = VerifiedCandidate::merge(filtered_candidates);
943  if (merge_res.is_error())
944  {
945  return ERR_APPEND(merge_res.get_error(), "Could not merge candidate set in post processing: failed to merge candidates");
946  }
947 
948  return merge_res;
949  }
950 
951  } // namespace
952 
953  VerifiedCandidate post_processing(const std::vector<VerifiedCandidate>& verified_candidates, const Netlist* nl, const std::vector<std::vector<Gate*>>& registers)
954  {
955  if (verified_candidates.empty())
956  {
957  return VerifiedCandidate();
958  }
959 
960  log_info(
961  "module_identification", "processing module for carry chain {} with {} verified variants", verified_candidates.front().m_base_gates.front()->get_name(), verified_candidates.size());
962 
963 #ifdef DEBUG_PRINT
964  std::cout << "ALL CANDIDATES [" << verified_candidates.size() << "]: " << std::endl;
965  for (const auto& vc : verified_candidates)
966  {
967  std::cout << vc.get_candidate_info() << std::endl;
968  }
969 #endif
970 
971  // TODO check where duplicate candidates are created in the first place
972  std::vector<VerifiedCandidate> unique_verified_candidates;
973  for (const auto& vc : verified_candidates)
974  {
975  bool is_unique = true;
976  for (const auto& uvc : unique_verified_candidates)
977  {
978  if (uvc == vc)
979  {
980  is_unique = false;
981  break;
982  }
983  }
984 
985  if (is_unique)
986  {
987  unique_verified_candidates.push_back(vc);
988  }
989  }
990 
991  log_info("module_identification", "processing module for carry chain with {} unique verified variants", unique_verified_candidates.size());
992 
993 #ifdef DEBUG_PRINT
994  std::cout << "ALL UNIQUE CANDIDATES [" << unique_verified_candidates.size() << "]: " << std::endl;
995  for (const auto& vc : unique_verified_candidates)
996  {
997  std::cout << vc.get_candidate_info() << std::endl;
998  }
999 #endif
1000  const auto best_candidate_set = select_best_candidate_set(nl, unique_verified_candidates, registers);
1001 
1002  log_info("module_identification", "selected best candidate set containing {} candidates.", best_candidate_set.size());
1003 
1004 #ifdef DEBUG_PRINT
1005  // TODO remove debug printing
1006  std::cout << "PRE-MERGE CANDIDATE SET [" << best_candidate_set.size() << "]: " << std::endl;
1007  for (const auto& c : best_candidate_set)
1008  {
1009  std::cout << c.get_candidate_info() << std::endl;
1010  }
1011 #endif
1012 
1013  const auto best_candidate = merge_candidate_set(nl, best_candidate_set, registers);
1014 
1015  return best_candidate.get();
1016  }
1017 
1018  } // namespace module_identification
1019 } // namespace hal
This file contains the enumeration and constants for the candidate types used in the module identific...
Represents a verified candidate for module identification.
static hal::Result< VerifiedCandidate > merge(const std::vector< VerifiedCandidate > &candidates)
Merge multiple verified candidates into a single candidate.
uint32_t u32
Definition: defines.h:41
#define UNUSED(expr)
Definition: defines.h:49
int32_t i32
Definition: defines.h:36
#define log_error(channel,...)
Definition: log.h:78
#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
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
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...
std::vector< Net * > get_input_nets(const std::vector< Gate * > &gates)
Get input nets from a list of gates.
Definition: utils.cpp:25
@ signed_less_equal
Signed less-than-or-equal comparison.
@ constant_multiplication
Constant multiplication operation.
@ signed_less_than
Signed less-than comparison.
@ constant_multiplication_offset
Constant multiplication operation with a constant offset.
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
Definition: defines.h:45
std::string enum_to_string(T e)
Definition: enums.h:53
Net * net
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...