HAL  v4.5.0-136-gbce33ee73
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
solve_fsm.cpp
Go to the documentation of this file.
1 // MIT License
2 //
3 // Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved.
4 // Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved.
5 // Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved.
6 // Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved.
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
25 #include "solve_fsm/solve_fsm.h"
26 
27 #include "hal_core/netlist/net.h"
28 
33 #include "hal_core/netlist/gate.h"
37 #include "hal_core/netlist/net.h"
38 
39 #include <bitset>
40 #include <fstream>
41 #include <deque>
42 #include <set>
43 
44 namespace hal
45 {
46  namespace solve_fsm
47  {
48  namespace
49  {
50  // generates a list of state flip flop output nets and the corresponding boolean function at their data input
51  Result<std::vector<std::pair<Net*, BooleanFunction>>>
52  generate_state_bfs(Netlist* nl, const std::vector<Gate*>& state_reg, const std::vector<Gate*>& transition_logic, const bool consider_control_inputs)
53  {
54  std::map<Net*, Net*> output_net_to_input_net;
55 
56  for (const auto& ff : state_reg)
57  {
58  const std::vector<GatePin*> d_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::data; });
59  if (d_pins.size() != 1)
60  {
61  return ERR("failed to create input - output mapping: currently not supporting flip-flops with multiple or no data inputs, but found " + std::to_string(d_pins.size())
62  + " for gate type " + ff->get_type()->get_name() + ".");
63  }
64 
65  hal::Net* input_net;
66  if (auto res = ff->get_fan_in_net(d_pins.front()); res == nullptr)
67  {
68  return ERR("failed to create input - output mapping: could not get fan-in net at pin " + d_pins.front()->get_name() + " of gate " + std::to_string(ff->get_id()) + ".");
69  }
70  else
71  {
72  input_net = res;
73  }
74 
75  for (const auto& out_net : ff->get_fan_out_nets())
76  {
77  output_net_to_input_net.insert({out_net, input_net});
78  }
79  }
80 
81  std::vector<std::pair<Net*, BooleanFunction>> state_bfs;
82 
83  const std::vector<const Gate*> subgraph_gates = {transition_logic.begin(), transition_logic.end()};
84  const auto nl_dec = SubgraphNetlistDecorator(*nl);
85 
86  for (const auto& ff : state_reg)
87  {
88  const std::vector<GatePin*> d_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::data; });
89  const GatePin* d_pin = d_pins.front();
90 
91  const std::vector<GatePin*> state_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::state; });
92  const std::vector<GatePin*> neg_state_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::neg_state; });
93 
94  Net* data_net = ff->get_fan_in_net(d_pin);
95 
96  BooleanFunction bf;
97  if (consider_control_inputs)
98  {
99  BooleanFunction complete_bf;
100  std::string internal_state_identifier;
101  std::string internal_negated_state_identifier;
102 
103  if (ff->get_type()->has_property(GateTypeProperty::ff))
104  {
105  const FFComponent* ff_component = ff->get_type()->get_component_as<FFComponent>([](const GateTypeComponent* c) { return FFComponent::is_class_of(c); });
106  const StateComponent* state_componenet = ff->get_type()->get_component_as<StateComponent>([](const GateTypeComponent* c) { return StateComponent::is_class_of(c); });
107 
108  complete_bf = ff_component->get_next_state_function();
109  internal_state_identifier = state_componenet->get_state_identifier();
110  internal_negated_state_identifier = state_componenet->get_neg_state_identifier();
111  }
112  else
113  {
114  return ERR("failed to generate boolean functions of state: gate " + ff->get_name() + " with ID " + std::to_string(ff->get_id())
115  + " of state register has an unhandeled type " + ff->get_type()->get_name());
116  }
117 
118  for (const auto& pin_var : complete_bf.get_variable_names())
119  {
120  // The complete Boolean function of a flip flop will contain the internal state and negated internal state.
121  // We substitute them with the outgoing state / negated state nets.
122  if (pin_var == internal_state_identifier)
123  {
124  if (state_pins.size() != 1)
125  {
126  return ERR("failed to generate boolean functions of state: found " + std::to_string(state_pins.size()) + " state pins at gate " + ff->get_name() + " with ID "
127  + std::to_string(ff->get_id()) + ", but we expect exactly 1.");
128  }
129 
130  complete_bf = complete_bf.substitute(internal_state_identifier, BooleanFunctionNetDecorator(*(ff->get_fan_out_net(state_pins.front()))).get_boolean_variable_name());
131 
132  continue;
133  }
134 
135  if (pin_var == internal_negated_state_identifier)
136  {
137  if (neg_state_pins.size() != 1)
138  {
139  return ERR("failed to generate boolean functions of state: found " + std::to_string(neg_state_pins.size()) + " neg state pins at gate " + ff->get_name()
140  + " with ID " + std::to_string(ff->get_id()) + ", but we expect exactly 1.");
141  }
142 
143  complete_bf =
144  complete_bf.substitute(internal_state_identifier, BooleanFunctionNetDecorator(*(ff->get_fan_out_net(neg_state_pins.front()))).get_boolean_variable_name());
145 
146  continue;
147  }
148 
149  const auto pin_net = ff->get_fan_in_net(pin_var);
150  BooleanFunction pin_bf;
151  if (auto res = nl_dec.get_subgraph_function(subgraph_gates, pin_net); res.is_error())
152  {
153  return ERR_APPEND(res.get_error(),
154  "failed to generate boolean functions of state: could not generate subgraph function for state net " + std::to_string(pin_net->get_id()) + ".");
155  }
156  else
157  {
158  pin_bf = res.get();
159  }
160 
161  if (auto res = BooleanFunctionDecorator(pin_bf).substitute_power_ground_nets(nl); res.is_error())
162  {
163  return ERR_APPEND(res.get_error(),
164  "failed to generate boolean functions of state: could not substitute power and ground nets in boolean funtion of net "
165  + std::to_string(pin_net->get_id()));
166  }
167  else
168  {
169  pin_bf = res.get();
170  }
171 
172  if (auto res = complete_bf.substitute(pin_var, pin_bf); res.is_error())
173  {
174  return ERR_APPEND(res.get_error(),
175  "failed to generate boolean functions of state: could not substitute variable " + pin_var + " in boolean funtion of net "
176  + std::to_string(pin_net->get_id()));
177  }
178  else
179  {
180  complete_bf = res.get();
181  }
182  }
183 
184  bf = complete_bf;
185  }
186  else
187  {
188  if (auto res = nl_dec.get_subgraph_function(subgraph_gates, data_net); res.is_error())
189  {
190  return ERR_APPEND(res.get_error(),
191  "failed to generate boolean functions of state: could not generate subgraph function for state net " + std::to_string(data_net->get_id()) + ".");
192  }
193  else
194  {
195  bf = res.get();
196  }
197 
198  if (auto res = BooleanFunctionDecorator(bf).substitute_power_ground_nets(nl); res.is_error())
199  {
200  return ERR_APPEND(res.get_error(),
201  "failed to generate boolean functions of state: could not substitute power and ground nets in boolean funtion of net "
202  + std::to_string(data_net->get_id()));
203  }
204  else
205  {
206  bf = res.get();
207  }
208  }
209 
210  bf.simplify();
211 
212  const auto var_names = bf.get_variable_names();
213 
214  // in the transition logic expressions of the next state bits we substitue the output nets of the state flip-flops with their (negated) input net.
215  for (const auto& [out, in] : output_net_to_input_net)
216  {
217  // check whether output net is part of the expression
218  if (var_names.find(BooleanFunctionNetDecorator(*out).get_boolean_variable_name()) == var_names.end())
219  {
220  continue;
221  }
222 
223  auto in_bf = BooleanFunctionNetDecorator(*in).get_boolean_variable();
224 
225  // check for multidriven nets
226  if (out->get_sources().size() != 1)
227  {
228  return ERR("failed to generate boolean functions of state: found multi driven net " + std::to_string(out->get_id()) + ".");
229  }
230 
231  // negate if the output stems from the negated state output
232  const GatePin* src_pin = out->get_sources().front()->get_pin();
233  if (src_pin->get_type() == PinType::neg_state)
234  {
235  in_bf = ~in_bf;
236  }
237 
238  auto res = bf.substitute(BooleanFunctionNetDecorator(*out).get_boolean_variable_name(), in_bf);
239 
240  if (res.is_error())
241  {
242  return ERR("failed to generate boolean functions of state: unable to replace out net " + std::to_string(out->get_id()) + " with in net " + std::to_string(in->get_id())
243  + ".");
244  }
245 
246  bf = res.get();
247  }
248 
249  state_bfs.push_back({data_net, bf});
250  }
251 
252  return OK(state_bfs);
253  }
254 
255  // takes a map of unconditional transitions and reconstructs the conditions under which each condition is taken
260  Result<std::vector<std::pair<std::string, BooleanFunction>>> generate_output_bfs(Netlist* nl, const std::vector<std::pair<std::string, std::vector<Net*>>>& outputs)
261  {
262  // the combinational gates of the netlist bound the subgraph, so expansion stops at the flip-flop
263  // output nets and at the inputs of the FSM, which is exactly where the output logic ends
264  const std::vector<Gate*> comb_gates = nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::combinational); });
265  const SubgraphNetlistDecorator dec(*nl);
266 
267  std::vector<std::pair<std::string, BooleanFunction>> res;
268  for (const auto& [name, nets] : outputs)
269  {
270  if (nets.empty())
271  {
272  return ERR("failed to generate output functions: output '" + name + "' does not contain any nets.");
273  }
274 
275  BooleanFunction bf;
276  for (u32 i = 0; i < nets.size(); i++)
277  {
278  if (nets.at(i) == nullptr)
279  {
280  return ERR("failed to generate output functions: output '" + name + "' contains a nullptr net at index " + std::to_string(i) + ".");
281  }
282 
283  auto bit_res = dec.get_subgraph_function(comb_gates, nets.at(i));
284  if (bit_res.is_error())
285  {
286  return ERR_APPEND(bit_res.get_error(), "failed to generate output functions: could not generate function for net " + std::to_string(nets.at(i)->get_id()) + ".");
287  }
288 
289  if (i == 0)
290  {
291  bf = bit_res.get();
292  continue;
293  }
294 
295  auto concat_res = BooleanFunction::Concat(bit_res.get(), std::move(bf), i + 1);
296  if (concat_res.is_error())
297  {
298  return ERR_APPEND(concat_res.get_error(), "failed to generate output functions: could not concatenate the nets of output '" + name + "'.");
299  }
300  bf = concat_res.get();
301  }
302 
303  res.push_back({name, std::move(bf)});
304  }
305 
306  return OK(res);
307  }
308 
313  std::map<std::string, BooleanFunction> generate_state_substitution(const std::vector<Gate*>& state_reg, const u64 state)
314  {
315  std::map<std::string, BooleanFunction> res;
316 
317  for (u32 i = 0; i < state_reg.size(); i++)
318  {
319  const bool bit = (state >> i) & 0x1;
320  const Gate* ff = state_reg.at(i);
321  const auto pins = ff->get_type()->get_pins([](const GatePin* p) {
322  return (p->get_direction() == PinDirection::output) && ((p->get_type() == PinType::state) || (p->get_type() == PinType::neg_state));
323  });
324 
325  for (const auto* pin : pins)
326  {
327  if (const Net* n = ff->get_fan_out_net(pin); n != nullptr)
328  {
329  const bool val = (pin->get_type() == PinType::neg_state) ? !bit : bit;
330  res.insert({BooleanFunctionNetDecorator(*n).get_boolean_variable_name(), BooleanFunction::Const(val ? 1 : 0, 1)});
331  }
332  }
333  }
334 
335  return res;
336  }
337 
342  Result<std::vector<std::pair<std::string, BooleanFunction>>>
343  evaluate_outputs_in_state(const std::vector<std::pair<std::string, BooleanFunction>>& output_bfs, const std::vector<Gate*>& state_reg, const u64 state)
344  {
345  const auto substitution = generate_state_substitution(state_reg, state);
346 
347  std::vector<std::pair<std::string, BooleanFunction>> res;
348  for (const auto& [name, bf] : output_bfs)
349  {
350  auto sub_res = bf.substitute(substitution);
351  if (sub_res.is_error())
352  {
353  return ERR_APPEND(sub_res.get_error(), "failed to evaluate outputs: could not substitute the state register in output '" + name + "'.");
354  }
355 
356  res.push_back({name, sub_res.get().simplify()});
357  }
358 
359  return OK(res);
360  }
361 
362  Result<std::map<u64, std::map<u64, BooleanFunction>>> generate_conditional_transitions(const std::vector<std::pair<Net*, BooleanFunction>>& state_bfs,
363  const std::map<u64, std::set<u64>>& transitions)
364  {
365  // generate all transitions that are reachable from the inital state.
366  std::map<u64, std::map<u64, BooleanFunction>> conditional_transitions;
367 
368  // for all possible and previously found successor states we build the condition to reach them
369  for (const auto& [prev, successors] : transitions)
370  {
371  // this builds a mapping for all the output net variables of the state vector to the current starting state
372  std::map<std::string, BooleanFunction> prev_mapping;
373  for (u32 i = 0; i < state_bfs.size(); i++)
374  {
375  prev_mapping.insert(
376  {BooleanFunctionNetDecorator(*(state_bfs.at(i).first)).get_boolean_variable_name(), ((prev >> i) & 1) ? BooleanFunction::Const(1, 1) : BooleanFunction::Const(0, 1)});
377  }
378 
379  for (const auto& suc : successors)
380  {
381  // this all the boolean functions of incoming data nets to the state vector either vanilla incase the corresponding state bit is 1 in the successor state or negated incase the state bit is 0 in the successor state.
382  BooleanFunction condition;
383 
384  for (u32 i = 0; i < state_bfs.size(); i++)
385  {
386  auto next_state_bit_bf = ((suc >> i) & 1) ? state_bfs.at(i).second : BooleanFunction::Not(state_bfs.at(i).second.clone(), 1).get();
387 
388  if (condition.is_empty())
389  {
390  condition = next_state_bit_bf;
391  }
392  else
393  {
394  condition = BooleanFunction::And(std::move(condition), std::move(next_state_bit_bf), 1).get();
395  }
396  }
397 
398  // replace all the variables of the previous state with their real values for our current state n and simplify.
399  condition = condition.substitute(prev_mapping).get();
400 
401  // we are left with a condition that only includes the inputs to the fsm that needs to be fullfilled to reach the successor state from state n.
402  condition = condition.simplify();
403 
404  conditional_transitions[prev].insert({suc, condition});
405  }
406  }
407 
408  return OK(conditional_transitions);
409  }
410 
411 
416  Result<std::map<u64, std::set<u64>>> generate_transitions_brute_force(const std::vector<std::pair<Net*, BooleanFunction>>& state_bfs, const u32 state_size)
417  {
418  // bitvector including all the functions to calculate the next state
419  BooleanFunction next_state_vec = state_bfs.front().second;
420  for (u32 i = 1; i < state_size; i++)
421  {
422  next_state_vec = BooleanFunction::Concat(state_bfs.at(i).second.clone(), std::move(next_state_vec), next_state_vec.size() + 1).get();
423  }
424 
425  std::map<u64, std::set<u64>> all_transitions;
426 
427  for (u64 state = 0; state < (u64(1) << state_size); state++)
428  {
429  // generate state map
430  std::map<std::string, BooleanFunction> var_to_val;
431  for (u32 state_index = 0; state_index < state_size; state_index++)
432  {
433  std::string var = BooleanFunctionNetDecorator(*(state_bfs.at(state_index).first)).get_boolean_variable_name();
434  BooleanFunction val = ((state >> state_index) & 0x1) ? BooleanFunction::Const(1, 1) : BooleanFunction::Const(0, 1);
435  var_to_val.insert({var, val});
436  }
437 
438  const auto sub_res = next_state_vec.substitute(var_to_val);
439  if (sub_res.is_error())
440  {
441  return ERR_APPEND(sub_res.get_error(), "failed to solve fsm: unable to substitute variables in next state vec.");
442  }
443 
444  const auto state_bf = sub_res.get().simplify();
445  const auto inputs = utils::to_vector(state_bf.get_variable_names());
446 
447  // brute force over all external inputs
448  for (u64 input_val = 0; input_val < (u64(1) << inputs.size()); input_val++)
449  {
450  // generate input map
451  std::unordered_map<std::string, std::vector<BooleanFunction::Value>> input_mapping;
452  for (u32 input_index = 0; input_index < inputs.size(); input_index++)
453  {
454  std::string input_var = inputs.at(input_index);
455  BooleanFunction::Value val = ((input_val >> input_index) & 0x1) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO;
456  input_mapping.insert({input_var, {val}});
457  }
458 
459  const auto& eval_res = state_bf.evaluate(input_mapping);
460  if (sub_res.is_error())
461  {
462  return ERR_APPEND(sub_res.get_error(), "failed to solve fsm: unable to evaluate next state function.");
463  }
464 
465  const auto eval = eval_res.get();
466 
467  if (eval.front() == BooleanFunction::Value::X)
468  {
469  return ERR("failed to solve fsm: evaluating state function resulted in X state.");
470  }
471 
472  const u64 suc_state = BooleanFunction::to_u64(eval).get();
473  all_transitions[state].insert(suc_state);
474  }
475  }
476 
477  return OK(all_transitions);
478  }
479 
484  Result<std::map<u64, std::set<u64>>>
485  generate_transitions_smt(const std::vector<std::pair<Net*, BooleanFunction>>& state_bfs, const u32 state_size, const u64 initial_state_num, const u32 timeout)
486  {
487  BooleanFunction prev_state_vec = BooleanFunctionNetDecorator(*(state_bfs.front().first)).get_boolean_variable();
488  BooleanFunction next_state_vec = state_bfs.front().second;
489  for (u32 i = 1; i < state_size; i++)
490  {
491  // bitvector representing the previous state
492  prev_state_vec = BooleanFunction::Concat(BooleanFunctionNetDecorator(*(state_bfs.at(i).first)).get_boolean_variable(), std::move(prev_state_vec), i + 1).get();
493 
494  // bitvector including all the functions to calculate the next state
495  next_state_vec = BooleanFunction::Concat(state_bfs.at(i).second.clone(), std::move(next_state_vec), i + 1).get();
496  }
497 
498  std::map<u64, std::set<u64>> all_transitions;
499 
500  std::deque<u64> q;
501  std::unordered_set<u64> visited;
502 
503  q.push_back(initial_state_num);
504 
505  while (!q.empty())
506  {
507  std::vector<u64> successor_states;
508 
509  u64 n = q.front();
510  q.pop_front();
511 
512  if (visited.find(n) != visited.end())
513  {
514  continue;
515  }
516  visited.insert(n);
517 
518  // generate new transitions and add them to the queue
519  SMT::Solver s;
520 
521  // set prev_state_vec to starting state
522  s = s.with_constraint(SMT::Constraint{prev_state_vec.clone(), BooleanFunction::Const(n, state_size)});
523 
524  while (true)
525  {
526  if (auto res = s.query(SMT::QueryConfig().with_model_generation().with_timeout(timeout)); res.is_error())
527  {
528  return ERR_APPEND(res.get_error(), "failed to solve fsm: failed to querry SMT solver for state " + std::to_string(n) + ".");
529  }
530  else
531  {
532  auto s_res = res.get();
533 
534  if (s_res.is_unsat())
535  {
536  break;
537  }
538 
539  if (s_res.is_unknown())
540  {
541  return ERR("failed to solve fsm: received an unknown solver result for state " + std::to_string(n) + ".");
542  }
543 
544  auto m = s_res.model.value();
545  auto suc = m.evaluate(next_state_vec).get();
546  auto suc_num = 0;
547 
548  // a constant (numeral) successor state
549  if (suc.is_constant())
550  {
551  suc_num = suc.get_constant_value_u64().get();
552  }
553  // a successor state that includes boolean functions (for example in form of input variables)
554  else
555  {
556  // to resolve such a successor state, we simpply set all variables left in the state to zero (which is one possible solution) and continue to search for more valid solutions
557  std::unordered_map<std::string, std::vector<BooleanFunction::Value>> zero_mapping;
558  for (const auto& var : suc.get_variable_names())
559  {
560  zero_mapping.insert({var, {BooleanFunction::Value::ZERO}});
561  }
562 
563  if (auto eval_res = suc.evaluate(zero_mapping); eval_res.is_error())
564  {
565  return ERR_APPEND(eval_res.get_error(), "failed to solve fsm: could not evaluate successor state to constant.");
566  }
567  else
568  {
569  suc_num = BooleanFunction::to_u64(eval_res.get()).get();
570  }
571  }
572 
573  q.push_back(suc_num);
574  all_transitions[n].insert(suc_num);
575  s = s.with_constraint(SMT::Constraint(BooleanFunction::Not(BooleanFunction::Eq(next_state_vec.clone(), BooleanFunction::Const(suc_num, suc.size()), 1).get(), 1).get()));
576  }
577  }
578  }
579 
580  return OK(all_transitions);
581  }
582 
587  std::map<u64, std::set<u64>> restrict_to_reachable(const std::map<u64, std::set<u64>>& all_transitions, const u64 initial_state_num)
588  {
589  std::map<u64, std::set<u64>> res;
590 
591  std::deque<u64> q = {initial_state_num};
592  std::unordered_set<u64> visited;
593 
594  while (!q.empty())
595  {
596  const u64 state = q.front();
597  q.pop_front();
598 
599  if (!visited.insert(state).second)
600  {
601  continue;
602  }
603 
604  const auto it = all_transitions.find(state);
605  if (it == all_transitions.end())
606  {
607  continue;
608  }
609 
610  res[state] = it->second;
611  for (const u64 successor : it->second)
612  {
613  q.push_back(successor);
614  }
615  }
616 
617  return res;
618  }
619  } // namespace
620 
621 
623  {
624  if (config.netlist == nullptr)
625  {
626  return ERR("failed to solve FSM: netlist is a nullptr.");
627  }
628 
629  if (config.state_register.empty())
630  {
631  return ERR("failed to solve FSM: no state register configured.");
632  }
633 
634  if (config.transition_logic.empty())
635  {
636  return ERR("failed to solve FSM: no transition logic configured.");
637  }
638 
639  const u32 state_size = config.state_register.size();
640  if (state_size > 64)
641  {
642  return ERR("failed to solve FSM: only up to 64 state flip-flops are supported, but got " + std::to_string(state_size) + ".");
643  }
644 
645  // extract Boolean functions for each state flip-flop
646  const auto state_bfs_res = generate_state_bfs(config.netlist, config.state_register, config.transition_logic, true);
647  if (state_bfs_res.is_error())
648  {
649  return ERR_APPEND(state_bfs_res.get_error(), "failed to solve FSM: unable to generate the Boolean functions of the state.");
650  }
651  const std::vector<std::pair<Net*, BooleanFunction>> state_bfs = state_bfs_res.get();
652 
653  // the first flip-flop of the state register provides the least significant bit
654  u64 initial_state_num = 0;
655  for (u32 i = 0; i < state_size; i++)
656  {
657  Gate* gate = config.state_register.at(i);
658  if (config.initial_state.empty())
659  {
660  break;
661  }
662 
663  if (config.initial_state.find(gate) == config.initial_state.end())
664  {
665  return ERR("failed to solve FSM: unable to find an initial value for gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())
666  + " in the provided initial state.");
667  }
668 
669  initial_state_num |= u64(config.initial_state.at(gate) ? 1 : 0) << i;
670  }
671 
672  std::map<u64, std::set<u64>> all_transitions;
673  if (config.brute_force)
674  {
675  auto transitions_res = generate_transitions_brute_force(state_bfs, state_size);
676  if (transitions_res.is_error())
677  {
678  return ERR_APPEND(transitions_res.get_error(), "failed to solve FSM: unable to determine the transitions by brute force.");
679  }
680 
681  // brute forcing visits every state, so the ones the FSM can never enter have to be dropped to match
682  // what the SMT approach returns for the same configuration
683  all_transitions = restrict_to_reachable(transitions_res.get(), initial_state_num);
684  }
685  else
686  {
687  auto transitions_res = generate_transitions_smt(state_bfs, state_size, initial_state_num, config.timeout);
688  if (transitions_res.is_error())
689  {
690  return ERR_APPEND(transitions_res.get_error(), "failed to solve FSM: unable to determine the transitions using the SMT solver.");
691  }
692 
693  all_transitions = transitions_res.get();
694  }
695 
697  res.netlist = config.netlist;
698  res.state_register = config.state_register;
699  res.output_nets = config.outputs;
700 
701  auto conditional_res = generate_conditional_transitions(state_bfs, all_transitions);
702  if (conditional_res.is_error())
703  {
704  return ERR_APPEND(conditional_res.get_error(), "failed to solve FSM: unable to determine the conditions of the transitions.");
705  }
706  res.transitions = conditional_res.get();
707 
708  if (!config.outputs.empty())
709  {
710  const auto output_bfs_res = generate_output_bfs(config.netlist, config.outputs);
711  if (output_bfs_res.is_error())
712  {
713  return ERR_APPEND(output_bfs_res.get_error(), "failed to solve FSM: unable to generate the Boolean functions of the outputs.");
714  }
715  const auto output_bfs = output_bfs_res.get();
716 
717  for (const auto& [state, _] : all_transitions)
718  {
719  auto state_outputs_res = evaluate_outputs_in_state(output_bfs, config.state_register, state);
720  if (state_outputs_res.is_error())
721  {
722  return ERR_APPEND(state_outputs_res.get_error(), "failed to solve FSM: unable to evaluate the outputs in state " + std::to_string(state) + ".");
723  }
724 
725  res.outputs[state] = state_outputs_res.get();
726  }
727  }
728 
729  return OK(res);
730  }
731  } // namespace solve_fsm
732 } // namespace hal
static Result< BooleanFunction > Eq(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Concat(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Value
represents the type of the node
static BooleanFunction Const(const BooleanFunction::Value &value)
static Result< u64 > to_u64(const std::vector< BooleanFunction::Value > &value)
static Result< BooleanFunction > Not(BooleanFunction &&p0, u16 size)
static Result< BooleanFunction > And(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static bool is_class_of(const GateTypeComponent *component)
Definition: gate.h:58
const std::string & get_name() const
Definition: gate.cpp:105
u32 get_id() const
Definition: gate.cpp:95
Definition: net.h:58
static bool is_class_of(const GateTypeComponent *component)
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
Result< StateTransitionGraph > solve_fsm(const Configuration &config)
Recover the state transition graph of an FSM from the netlist that implements it.
Definition: solve_fsm.cpp:622
std::vector< T > to_vector(const Container< T, Args... > &container)
Definition: utils.h:559
Definition: defines.h:45
std::vector< PinInformation > pins
std::string name
QTextStream & dec(QTextStream &stream)
This file contains the function to recover the state transition graph of a finite state machine.
The configuration of a run of the FSM solver.
Definition: configuration.h:59
u32 timeout
The timeout for the underlying SMT solver in milliseconds. Defaults to 600000 ms.
std::vector< Gate * > state_register
The flip-flops that make up the state register of the FSM.
Definition: configuration.h:78
Netlist * netlist
The netlist that implements the FSM.
Definition: configuration.h:70
std::vector< std::pair< std::string, std::vector< Net * > > > outputs
The outputs of the FSM, each given as a name and the nets that make up that output.
Definition: configuration.h:93
bool brute_force
Enumerate all states instead of using an SMT solver. Defaults to false.
std::map< Gate *, bool > initial_state
The initial value of each flip-flop of the state register.
std::vector< Gate * > transition_logic
The combinational gates that compute the next state of the FSM.
Definition: configuration.h:85
The state transition graph of an FSM, i.e., the behavior that its netlist implements.
std::vector< std::pair< std::string, std::vector< Net * > > > output_nets
The outputs of the FSM, each given as a name and the nets that make up that output.
std::map< u64, std::map< u64, BooleanFunction > > transitions
A map from each state to its successor states, together with the condition under which the respective...
std::vector< Gate * > state_register
The flip-flops that make up the state register, in the order that determines the encoding of a state.
Netlist * netlist
The netlist that implements the FSM.
std::map< u64, std::vector< std::pair< std::string, BooleanFunction > > > outputs
A map from each state to the value of every output of the FSM in that state.