HAL  v4.5.0-258-gf372a0264
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
pre_processing.cpp
Go to the documentation of this file.
2 
10 #include "hal_core/netlist/gate.h"
12 #include "hal_core/netlist/net.h"
15 #include "hal_core/utilities/log.h"
16 
17 #include <algorithm>
18 #include <chrono>
19 #include <deque>
20 #include <queue>
21 
22 namespace hal
23 {
24  namespace dataflow
25  {
26  namespace pre_processing
27  {
28  namespace
29  {
30  void identify_all_target_gates(const dataflow::Configuration& config, NetlistAbstraction& netlist_abstr)
31  {
32  log_info("dataflow", "identifying target gates");
33  netlist_abstr.target_gates = netlist_abstr.nl->get_gates([config](auto g) { return config.gate_types.find(g->get_type()) != config.gate_types.end(); });
34  std::sort(netlist_abstr.target_gates.begin(), netlist_abstr.target_gates.end(), [](const Gate* g1, const Gate* g2) { return g1->get_id() < g2->get_id(); });
35  log_info("dataflow", " #gates: {}", netlist_abstr.nl->get_gates().size());
36  log_info("dataflow", " #target gates: {}", netlist_abstr.target_gates.size());
37  }
38 
39  void identify_all_control_signals(const dataflow::Configuration& config, NetlistAbstraction& netlist_abstr)
40  {
41  auto begin_time = std::chrono::high_resolution_clock::now();
42  log_info("dataflow", "identifying control signals");
43  for (auto sg : netlist_abstr.target_gates)
44  {
45  std::vector<u32> fingerprint;
46  auto id = sg->get_id();
47 
48  for (auto type : config.control_pin_types)
49  {
50  for (auto net :
51  netlist_utils::get_nets_at_pins(sg, sg->get_type()->get_pins([type](const GatePin* p) { return p->get_direction() == PinDirection::input && p->get_type() == type; })))
52  {
53  netlist_abstr.gate_to_control_signals[id][type].insert(net->get_id());
54  fingerprint.push_back(net->get_id());
55  }
56  }
57 
58  netlist_abstr.gate_to_fingerprint[id] = fingerprint;
59  }
60 
61  log_info("dataflow", " done after {:3.2f}s", seconds_since(begin_time));
62  }
63 
64  void identify_known_groups(const dataflow::Configuration& config, NetlistAbstraction& netlist_abstr, std::vector<std::vector<Gate*>>& known_target_groups)
65  {
66  // for known gates, only check if they all match the target gate types; discard groups that do not
67  for (const auto& gates : config.known_gate_groups)
68  {
69  if (std::all_of(
70  gates.begin(), gates.end(), [&netlist_abstr](const Gate* g) { return netlist_abstr.gate_to_fingerprint.find(g->get_id()) != netlist_abstr.gate_to_fingerprint.end(); }))
71  {
72  known_target_groups.push_back(gates);
73  }
74  else
75  {
76  log_warning("dataflow",
77  "known group containing gate '{}' with ID {} contains gates that are not of the target gate type, known group will be ignored...",
78  gates.front()->get_name(),
79  gates.front()->get_id());
80  }
81  }
82  }
83 
84  /* get all successor/predecessor FFs of all FFs */
85  void identify_successors_predecessors(const dataflow::Configuration& config, NetlistAbstraction& netlist_abstr)
86  {
87  log_info("dataflow", "identifying successors and predecessors of sequential gates...");
88  measure_block_time("identifying successors and predecessors of sequential gates") user_feedback::ProgressPrinter progress_bar("dataflow: preprocessing …");
89  float cnt = 0;
90 
91  // cache map of nets to group indices of known net groups
92  std::unordered_map<const Net*, u32> net_to_group_index;
93  for (u32 i = 0; i < config.known_net_groups.size(); i++)
94  {
95  const auto& net_group = config.known_net_groups.at(i);
96  if (net_group.size() < config.min_group_size)
97  {
98  continue;
99  }
100 
101  for (const auto* net : net_group)
102  {
103  net_to_group_index[net] = i;
104  }
105  }
106 
107  // find successors
108  std::unordered_map<const Net*, std::pair<std::unordered_set<Gate*>, std::unordered_set<u32>>> suc_cache;
109  std::unordered_map<const Net*, std::unordered_set<u32>> pred_cache;
110 
111  // The caches are shared by the traversals of all target gates, so an entry is trusted to be
112  // complete. A traversal does not push a net that it has visited before, though, so whatever such
113  // a net leads to has to be handed to the nets of the current path explicitly. Otherwise, the
114  // entries of these nets miss it, and so does every later traversal that finds them in the cache.
115  // A visited net that is not on the current path has been explored completely; one on the path
116  // closes a combinational loop and is still being explored, so its entry is not complete yet.
117  const auto on_path = [](const Net* net, const Net* current, const std::vector<const Net*>& previous) {
118  return net == current || std::find(previous.cbegin(), previous.cend(), net) != previous.cend();
119  };
120  const auto pass_on_successors = [&suc_cache, &on_path](const Net* visited_net, const Net* current, const std::vector<const Net*>& previous) {
121  const auto it = suc_cache.find(visited_net);
122  if (it == suc_cache.end() || on_path(visited_net, current, previous))
123  {
124  return;
125  }
126  // references to the elements of an unordered_map stay valid when inserting rehashes it
127  const auto& [cached_gates, cached_net_groups] = it->second;
128  for (const Net* n : previous)
129  {
130  auto& entry = suc_cache[n];
131  std::get<0>(entry).insert(cached_gates.cbegin(), cached_gates.cend());
132  std::get<1>(entry).insert(cached_net_groups.cbegin(), cached_net_groups.cend());
133  }
134  auto& entry = suc_cache[current];
135  std::get<0>(entry).insert(cached_gates.cbegin(), cached_gates.cend());
136  std::get<1>(entry).insert(cached_net_groups.cbegin(), cached_net_groups.cend());
137  };
138  const auto pass_on_predecessors = [&pred_cache, &on_path](const Net* visited_net, const Net* current, const std::vector<const Net*>& previous) {
139  const auto it = pred_cache.find(visited_net);
140  if (it == pred_cache.end() || on_path(visited_net, current, previous))
141  {
142  return;
143  }
144  const auto& cached_net_groups = it->second;
145  for (const Net* n : previous)
146  {
147  pred_cache[n].insert(cached_net_groups.cbegin(), cached_net_groups.cend());
148  }
149  pred_cache[current].insert(cached_net_groups.cbegin(), cached_net_groups.cend());
150  };
151 
152  for (const auto& gate : netlist_abstr.target_gates)
153  {
154  cnt++;
155  progress_bar.report(cnt / netlist_abstr.target_gates.size());
156 
157  // create sets even if there are no successors
158  if (netlist_abstr.gate_to_successors.find(gate->get_id()) == netlist_abstr.gate_to_successors.end())
159  {
160  netlist_abstr.gate_to_successors[gate->get_id()] = std::unordered_set<u32>();
161  }
162  if (netlist_abstr.gate_to_predecessors.find(gate->get_id()) == netlist_abstr.gate_to_predecessors.end())
163  {
164  netlist_abstr.gate_to_predecessors[gate->get_id()] = std::unordered_set<u32>();
165  }
166  if (netlist_abstr.gate_to_known_successor_groups.find(gate->get_id()) == netlist_abstr.gate_to_known_successor_groups.end())
167  {
168  netlist_abstr.gate_to_known_successor_groups[gate->get_id()] = std::unordered_set<u32>();
169  }
170  if (netlist_abstr.gate_to_known_predecessor_groups.find(gate->get_id()) == netlist_abstr.gate_to_known_predecessor_groups.end())
171  {
172  netlist_abstr.gate_to_known_predecessor_groups[gate->get_id()] = std::unordered_set<u32>();
173  }
174 
175  const auto& start_fan_out_nets = gate->get_fan_out_nets();
176  std::vector<const Net*> stack(start_fan_out_nets.cbegin(), start_fan_out_nets.cend()); // init stack with fan-out of start gate
177  std::unordered_set<const Net*> visited;
178  std::vector<const Net*> previous; // will keep track of all predecessor nets of the current net
179  while (!stack.empty())
180  {
181  const Net* current = stack.back(); // do not pop last item yet
182 
183  if (!previous.empty() && current == previous.back())
184  {
185  // will execute when coming back to a net along the path of predecessor nets (i.e., after finding a target gate or when unable to propagate further)
186  stack.pop_back();
187  previous.pop_back();
188  continue;
189  }
190 
191  visited.insert(current);
192 
193  if (const auto suc_cache_it = suc_cache.find(current); suc_cache_it != suc_cache.end())
194  {
195  auto& suc_cache_current = std::get<1>(*suc_cache_it);
196  const auto& suc_cached_gates = std::get<0>(suc_cache_current);
197  const auto& suc_cached_net_groups = std::get<1>(suc_cache_current);
198 
199  // add cached target gates and known successor net groups to suc_cache of all predecessor nets
200  for (const auto* n : previous)
201  {
202  auto& suc_cache_n = suc_cache[n];
203  std::get<0>(suc_cache_n).insert(suc_cached_gates.cbegin(), suc_cached_gates.cend());
204  std::get<1>(suc_cache_n).insert(suc_cached_net_groups.cbegin(), suc_cached_net_groups.cend());
205  }
206 
207  // add cached net groups to known successor net groups of current gate
208  netlist_abstr.gate_to_known_successor_groups[gate->get_id()].insert(suc_cached_net_groups.cbegin(), suc_cached_net_groups.cend());
209 
210  stack.pop_back();
211  }
212  else
213  {
214  if (const auto group_it = net_to_group_index.find(current); group_it != net_to_group_index.end())
215  {
216  std::get<1>(suc_cache[current]).insert(group_it->second);
217  for (const auto* n : previous)
218  {
219  std::get<1>(suc_cache[n]).insert(group_it->second);
220  }
221  netlist_abstr.gate_to_known_successor_groups[gate->get_id()].insert(group_it->second);
222  }
223 
224  bool added = false;
225  for (const auto* ep : current->get_destinations())
226  {
227  auto* g = ep->get_gate();
228  if (config.gate_types.find(g->get_type()) != config.gate_types.end())
229  {
230  // if target gate found, add to suc_cache of all nets along the predecessor path
231  auto& suc_cache_current = suc_cache[current];
232  std::get<0>(suc_cache_current).insert(g);
233  for (const auto* n : previous)
234  {
235  std::get<0>(suc_cache[n]).insert(g);
236  }
237  }
238  else
239  {
240  // propagate further by adding successors to stack
241  for (const auto* n : g->get_fan_out_nets())
242  {
243  if (visited.find(n) == visited.end())
244  {
245  stack.push_back(n);
246  added = true;
247  }
248  else
249  {
250  pass_on_successors(n, current, previous);
251  }
252  }
253  }
254  }
255 
256  if (added)
257  {
258  // keep track of previous net whenever propagating further
259  previous.push_back(current);
260  }
261  else
262  {
263  // if no change to stack, go back
264  stack.pop_back();
265  }
266  }
267  }
268 
269  const auto& start_fan_in_nets = gate->get_fan_in_nets();
270  stack = std::vector<const Net*>(start_fan_in_nets.begin(), start_fan_in_nets.end());
271  visited.clear();
272  previous.clear();
273  while (!stack.empty())
274  {
275  const Net* current = stack.back(); // do not pop last item yet
276 
277  if (!previous.empty() && current == previous.back())
278  {
279  // will execute when coming back to a net along the path of predecessor nets (i.e., after finding a target gate or when unable to propagate further)
280  stack.pop_back();
281  previous.pop_back();
282  continue;
283  }
284 
285  visited.insert(current);
286 
287  if (const auto pred_cache_it = pred_cache.find(current); pred_cache_it != pred_cache.end())
288  {
289  auto& pred_cached_net_groups = std::get<1>(*pred_cache_it);
290 
291  // add cached known predecessor net groups to cache of all predecessor nets
292  for (const auto* n : previous)
293  {
294  pred_cache[n].insert(pred_cached_net_groups.cbegin(), pred_cached_net_groups.cend());
295  }
296 
297  // add cached net groups to known predecessor net groups of current gate
298  netlist_abstr.gate_to_known_predecessor_groups[gate->get_id()].insert(pred_cached_net_groups.cbegin(), pred_cached_net_groups.cend());
299 
300  stack.pop_back();
301  }
302  else
303  {
304  if (const auto group_it = net_to_group_index.find(current); group_it != net_to_group_index.end())
305  {
306  pred_cache[current].insert(group_it->second);
307  for (const auto* n : previous)
308  {
309  pred_cache[n].insert(group_it->second);
310  }
311  netlist_abstr.gate_to_known_predecessor_groups[gate->get_id()].insert(group_it->second);
312  }
313 
314  bool added = false;
315  for (const auto* ep : current->get_sources())
316  {
317  auto* g = ep->get_gate();
318  if (config.gate_types.find(g->get_type()) == config.gate_types.end())
319  {
320  // propagate further by adding predecessors to stack
321  for (const auto* n : g->get_fan_in_nets())
322  {
323  if (visited.find(n) == visited.end())
324  {
325  stack.push_back(n);
326  added = true;
327  }
328  else
329  {
330  pass_on_predecessors(n, current, previous);
331  }
332  }
333  }
334  }
335 
336  if (added)
337  {
338  // keep track of previous net whenever propagating further
339  previous.push_back(current);
340  }
341  else
342  {
343  // if no change to stack, go back
344  stack.pop_back();
345  }
346  }
347  }
348 
349  // collect successor target gates by getting cache of all fan-out nets of start gate
350  std::unordered_set<Gate*> next_target_gates;
351  for (const auto* n : start_fan_out_nets)
352  {
353  if (const auto it = suc_cache.find(n); it != suc_cache.end())
354  {
355  const auto& cached_gates = std::get<0>(std::get<1>(*it));
356  next_target_gates.insert(cached_gates.cbegin(), cached_gates.cend());
357  }
358  }
359 
360  for (const auto& suc : next_target_gates)
361  {
362  netlist_abstr.gate_to_successors[gate->get_id()].insert(suc->get_id());
363  netlist_abstr.gate_to_predecessors[suc->get_id()].insert(gate->get_id());
364  }
365  }
366  progress_bar.clear();
367  }
368  } // namespace
369 
370  NetlistAbstraction run(const dataflow::Configuration& config, std::shared_ptr<dataflow::Grouping>& initial_grouping)
371  {
372  log_info("dataflow", "pre-processing netlist...");
373  measure_block_time("pre-processing");
374  NetlistAbstraction netlist_abstr(config.netlist);
375  std::vector<std::vector<Gate*>> known_target_groups;
376  std::vector<std::vector<Net*>> known_net_groups;
377  identify_all_target_gates(config, netlist_abstr);
378  identify_all_control_signals(config, netlist_abstr);
379  identify_known_groups(config, netlist_abstr, known_target_groups);
380  identify_successors_predecessors(config, netlist_abstr);
381 
382  if (config.enable_stages)
383  {
384  identify_register_stages(netlist_abstr);
385  }
386 
387  initial_grouping = std::make_shared<dataflow::Grouping>(netlist_abstr, known_target_groups);
388 
389  return netlist_abstr;
390  }
391  } // namespace pre_processing
392  } // namespace dataflow
393 } // namespace hal
u32 size
This file contains the struct that holds all information on a dataflow analysis configuration.
uint32_t u32
Definition: defines.h:41
#define log_info(channel,...)
Definition: log.h:70
#define log_warning(channel,...)
Definition: log.h:76
void identify_register_stages(NetlistAbstraction &netlist_abstr)
NetlistAbstraction run(const dataflow::Configuration &config, std::shared_ptr< dataflow::Grouping > &initial_grouping)
std::vector< Net * > get_nets_at_pins(Gate *gate, std::vector< GatePin * > pins)
Definition: defines.h:45
PinType type
Net * net
i32 id
This file contains the struct that holds all information on the netlist abstraction used for dataflow...
This file contains the class that holds all information of a dataflow analysis grouping.
Configuration of a dataflow analysis run.
Definition: configuration.h:61
bool enable_stages
Enable stage identification as part of dataflow analysis. Defaults to false.
Netlist * netlist
The netlist to be analyzed.
Definition: configuration.h:72
The abstraction of the netlist that only contains gates of a specified type, e.g.,...
#define measure_block_time(X)
Definition: timing_utils.h:36
#define seconds_since(X)
Definition: timing_utils.h:38