HAL
solve_fsm.cpp
Go to the documentation of this file.
1 #include "solve_fsm/solve_fsm.h"
2 
11 #include "hal_core/netlist/net.h"
12 
13 #include <bitset>
14 #include <fstream>
15 #include <queue>
16 
17 namespace hal
18 {
19  namespace solve_fsm
20  {
21  namespace
22  {
23  // generates a list of state flip flop output nets and the corresponding boolean function at their data input
24  Result<std::vector<std::pair<Net*, BooleanFunction>>>
25  generate_state_bfs(Netlist* nl, const std::vector<Gate*>& state_reg, const std::vector<Gate*>& transition_logic, const bool consider_control_inputs)
26  {
27  std::map<Net*, Net*> output_net_to_input_net;
28 
29  for (const auto& ff : state_reg)
30  {
31  const std::vector<GatePin*> d_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::data; });
32  if (d_pins.size() != 1)
33  {
34  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())
35  + " for gate type " + ff->get_type()->get_name() + ".");
36  }
37 
38  hal::Net* input_net;
39  if (auto res = ff->get_fan_in_net(d_pins.front()); res == nullptr)
40  {
41  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()) + ".");
42  }
43  else
44  {
45  input_net = res;
46  }
47 
48  for (const auto& out_net : ff->get_fan_out_nets())
49  {
50  output_net_to_input_net.insert({out_net, input_net});
51  }
52  }
53 
54  std::vector<std::pair<Net*, BooleanFunction>> state_bfs;
55 
56  const std::vector<const Gate*> subgraph_gates = {transition_logic.begin(), transition_logic.end()};
57  const auto nl_dec = SubgraphNetlistDecorator(*nl);
58 
59  for (const auto& ff : state_reg)
60  {
61  const std::vector<GatePin*> d_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::data; });
62  const GatePin* d_pin = d_pins.front();
63 
64  const std::vector<GatePin*> state_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::state; });
65  const std::vector<GatePin*> neg_state_pins = ff->get_type()->get_pins([](const GatePin* pin) { return pin->get_type() == PinType::neg_state; });
66 
67  Net* data_net = ff->get_fan_in_net(d_pin);
68 
69  BooleanFunction bf;
70  if (consider_control_inputs)
71  {
72  BooleanFunction complete_bf;
73  std::string internal_state_identifier;
74  std::string internal_negated_state_identifier;
75 
76  if (ff->get_type()->has_property(GateTypeProperty::ff))
77  {
78  const FFComponent* ff_component = ff->get_type()->get_component_as<FFComponent>([](const GateTypeComponent* c) { return FFComponent::is_class_of(c); });
79  const StateComponent* state_componenet = ff->get_type()->get_component_as<StateComponent>([](const GateTypeComponent* c) { return StateComponent::is_class_of(c); });
80 
81  complete_bf = ff_component->get_next_state_function();
82  internal_state_identifier = state_componenet->get_state_identifier();
83  internal_negated_state_identifier = state_componenet->get_neg_state_identifier();
84  }
85  else
86  {
87  return ERR("failed to generate boolean functions of state: gate " + ff->get_name() + " with ID " + std::to_string(ff->get_id())
88  + " of state register has an unhandeled type " + ff->get_type()->get_name());
89  }
90 
91  std::cout << complete_bf << std::endl;
92 
93  for (const auto& pin_var : complete_bf.get_variable_names())
94  {
95  // The complete Boolean function of a flip flop will contain the internal state and negated internal state.
96  // We substitute them with the outgoing state / negated state nets.
97  if (pin_var == internal_state_identifier)
98  {
99  if (state_pins.size() != 1)
100  {
101  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 "
102  + std::to_string(ff->get_id()) + ", but we expect exactly 1.");
103  }
104 
105  complete_bf = complete_bf.substitute(internal_state_identifier, BooleanFunctionNetDecorator(*(ff->get_fan_out_net(state_pins.front()))).get_boolean_variable_name());
106 
107  continue;
108  }
109 
110  if (pin_var == internal_negated_state_identifier)
111  {
112  if (neg_state_pins.size() != 1)
113  {
114  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()
115  + " with ID " + std::to_string(ff->get_id()) + ", but we expect exactly 1.");
116  }
117 
118  complete_bf =
119  complete_bf.substitute(internal_state_identifier, BooleanFunctionNetDecorator(*(ff->get_fan_out_net(neg_state_pins.front()))).get_boolean_variable_name());
120 
121  continue;
122  }
123 
124  const auto pin_net = ff->get_fan_in_net(pin_var);
125  BooleanFunction pin_bf;
126  if (auto res = nl_dec.get_subgraph_function(subgraph_gates, pin_net); res.is_error())
127  {
128  return ERR_APPEND(res.get_error(),
129  "failed to generate boolean functions of state: could not generate subgraph function for state net " + std::to_string(pin_net->get_id()) + ".");
130  }
131  else
132  {
133  pin_bf = res.get();
134  }
135 
136  if (auto res = BooleanFunctionDecorator(pin_bf).substitute_power_ground_nets(nl); res.is_error())
137  {
138  return ERR_APPEND(res.get_error(),
139  "failed to generate boolean functions of state: could not substitute power and ground nets in boolean funtion of net "
140  + std::to_string(pin_net->get_id()));
141  }
142  else
143  {
144  pin_bf = res.get();
145  }
146 
147  if (auto res = complete_bf.substitute(pin_var, pin_bf); res.is_error())
148  {
149  return ERR_APPEND(res.get_error(),
150  "failed to generate boolean functions of state: could not substitute variable " + pin_var + " in boolean funtion of net "
151  + std::to_string(pin_net->get_id()));
152  }
153  else
154  {
155  complete_bf = res.get();
156  }
157  }
158 
159  std::cout << complete_bf << std::endl;
160 
161  bf = complete_bf;
162  }
163  else
164  {
165  if (auto res = nl_dec.get_subgraph_function(subgraph_gates, data_net); res.is_error())
166  {
167  return ERR_APPEND(res.get_error(),
168  "failed to generate boolean functions of state: could not generate subgraph function for state net " + std::to_string(data_net->get_id()) + ".");
169  }
170  else
171  {
172  bf = res.get();
173  }
174 
175  if (auto res = BooleanFunctionDecorator(bf).substitute_power_ground_nets(nl); res.is_error())
176  {
177  return ERR_APPEND(res.get_error(),
178  "failed to generate boolean functions of state: could not substitute power and ground nets in boolean funtion of net "
179  + std::to_string(data_net->get_id()));
180  }
181  else
182  {
183  bf = res.get();
184  }
185  }
186 
187  bf.simplify();
188 
189  const auto var_names = bf.get_variable_names();
190 
191  // 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.
192  for (const auto& [out, in] : output_net_to_input_net)
193  {
194  // check whether output net is part of the expression
195  if (var_names.find(BooleanFunctionNetDecorator(*out).get_boolean_variable_name()) == var_names.end())
196  {
197  continue;
198  }
199 
200  auto in_bf = BooleanFunctionNetDecorator(*in).get_boolean_variable();
201 
202  // check for multidriven nets
203  if (out->get_sources().size() != 1)
204  {
205  return ERR("failed to generate boolean functions of state: found multi driven net " + std::to_string(out->get_id()) + ".");
206  }
207 
208  // negate if the output stems from the negated state output
209  const GatePin* src_pin = out->get_sources().front()->get_pin();
210  if (src_pin->get_type() == PinType::neg_state)
211  {
212  in_bf = ~in_bf;
213  }
214 
215  auto res = bf.substitute(BooleanFunctionNetDecorator(*out).get_boolean_variable_name(), in_bf);
216 
217  if (res.is_error())
218  {
219  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())
220  + ".");
221  }
222 
223  bf = res.get();
224  }
225 
226  state_bfs.push_back({data_net, bf});
227  }
228 
229  return OK(state_bfs);
230  }
231 
232  // takes a map of unconditional transitions and reconstructs the conditions under which each condition is taken
233  Result<std::map<u64, std::map<u64, BooleanFunction>>> generate_conditional_transitions(const std::vector<std::pair<Net*, BooleanFunction>>& state_bfs,
234  const std::map<u64, std::set<u64>>& transitions)
235  {
236  // generate all transitions that are reachable from the inital state.
237  std::map<u64, std::map<u64, BooleanFunction>> conditional_transitions;
238 
239  // for all possible and previously found successor states we build the condition to reach them
240  for (const auto& [prev, successors] : transitions)
241  {
242  // this builds a mapping for all the output net variables of the state vector to the current starting state
243  std::map<std::string, BooleanFunction> prev_mapping;
244  for (u32 i = 0; i < state_bfs.size(); i++)
245  {
246  prev_mapping.insert(
247  {BooleanFunctionNetDecorator(*(state_bfs.at(i).first)).get_boolean_variable_name(), ((prev >> i) & 1) ? BooleanFunction::Const(1, 1) : BooleanFunction::Const(0, 1)});
248  }
249 
250  for (const auto& suc : successors)
251  {
252  // 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.
253  BooleanFunction condition;
254 
255  for (u32 i = 0; i < state_bfs.size(); i++)
256  {
257  auto next_state_bit_bf = ((suc >> i) & 1) ? state_bfs.at(i).second : BooleanFunction::Not(state_bfs.at(i).second.clone(), 1).get();
258 
259  if (condition.is_empty())
260  {
261  condition = next_state_bit_bf;
262  }
263  else
264  {
265  condition = BooleanFunction::And(std::move(condition), std::move(next_state_bit_bf), 1).get();
266  }
267  }
268 
269  // replace all the variables of the previous state with their real values for our current state n and simplify.
270  condition = condition.substitute(prev_mapping).get();
271 
272  // 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.
273  condition = condition.simplify();
274 
275  conditional_transitions[prev].insert({suc, condition});
276  }
277  }
278 
279  return OK(conditional_transitions);
280  }
281 
282  } // namespace
283 
284  Result<std::map<u64, std::map<u64, BooleanFunction>>>
285  solve_fsm_brute_force(Netlist* nl, const std::vector<Gate*>& state_reg, const std::vector<Gate*>& transition_logic, const std::filesystem::path& graph_path)
286  {
287  const u32 state_size = state_reg.size();
288  if (state_size > 64)
289  {
290  return ERR("failed to solve fsm: Currently only supports fsm with up to 64 state flip-flops but got " + std::to_string(state_size) + ".");
291  }
292 
293  // extract Boolean functions for each state flip-flop
294  const auto state_bfs_res = generate_state_bfs(nl, state_reg, transition_logic, true);
295  if (state_bfs_res.is_error())
296  {
297  return ERR_APPEND(state_bfs_res.get_error(), "failed to solve fsm: unable to generate Boolean functions for state.");
298  }
299  const std::vector<std::pair<Net*, BooleanFunction>> state_bfs = state_bfs_res.get();
300 
301  // bitvector including all the functions to calculate the next state
302  BooleanFunction next_state_vec = state_bfs.front().second;
303  for (u32 i = 1; i < state_reg.size(); i++)
304  {
305  next_state_vec = BooleanFunction::Concat(state_bfs.at(i).second.clone(), std::move(next_state_vec), next_state_vec.size() + 1).get();
306  }
307 
308  std::map<u64, std::set<u64>> all_transitions;
309 
310  for (u64 state = 0; state < (u64(1) << state_size); state++)
311  {
312  // generate state map
313  std::map<std::string, BooleanFunction> var_to_val;
314  for (u32 state_index = 0; state_index < state_size; state_index++)
315  {
316  std::string var = BooleanFunctionNetDecorator(*(state_bfs.at(state_index).first)).get_boolean_variable_name();
317  BooleanFunction val = ((state >> state_index) & 0x1) ? BooleanFunction::Const(1, 1) : BooleanFunction::Const(0, 1);
318  var_to_val.insert({var, val});
319  }
320 
321  const auto sub_res = next_state_vec.substitute(var_to_val);
322  if (sub_res.is_error())
323  {
324  return ERR_APPEND(sub_res.get_error(), "failed to solve fsm: unable to substitute variables in next state vec.");
325  }
326 
327  const auto state_bf = sub_res.get().simplify();
328  const auto inputs = utils::to_vector(state_bf.get_variable_names());
329 
330  // brute force over all external inputs
331  for (u64 input_val = 0; input_val < (u64(1) << inputs.size()); input_val++)
332  {
333  // generate input map
334  std::unordered_map<std::string, std::vector<BooleanFunction::Value>> input_mapping;
335  for (u32 input_index = 0; input_index < inputs.size(); input_index++)
336  {
337  std::string input_var = inputs.at(input_index);
338  BooleanFunction::Value val = ((input_val >> input_index) & 0x1) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO;
339  input_mapping.insert({input_var, {val}});
340  }
341 
342  const auto& eval_res = state_bf.evaluate(input_mapping);
343  if (sub_res.is_error())
344  {
345  return ERR_APPEND(sub_res.get_error(), "failed to solve fsm: unable to evaluate next state function.");
346  }
347 
348  const auto eval = eval_res.get();
349 
350  if (eval.front() == BooleanFunction::Value::X)
351  {
352  return ERR("failed to solve fsm: evaluating state function resulted in X state.");
353  }
354 
355  const u64 suc_state = BooleanFunction::to_u64(eval).get();
356  all_transitions[state].insert(suc_state);
357  }
358  }
359 
360  const auto conditional_transitions = generate_conditional_transitions(state_bfs, all_transitions).get();
361 
362  /* DEBUG PRINTING */
363  for (const auto& [org, successors] : conditional_transitions)
364  {
365  std::cout << org << ": " << std::endl;
366  for (const auto& [suc, condition] : successors)
367  {
368  std::cout << "\t" << suc << ": " << condition.to_string() << std::endl;
369  }
370  }
371  /* END DEBUG PRINTING */
372 
373  if (auto graph_res = generate_dot_graph(state_reg, conditional_transitions, graph_path); graph_res.is_error())
374  {
375  return ERR_APPEND(graph_res.get_error(), "failed to solve fsm: unable to generate dot graph.");
376  }
377 
378  return OK(conditional_transitions);
379  }
380 
382  const std::vector<Gate*>& state_reg,
383  const std::vector<Gate*>& transition_logic,
384  const std::map<Gate*, bool>& initial_state,
385  const std::filesystem::path& graph_path,
386  const u32 timeout)
387  {
388  const u32 state_size = state_reg.size();
389  if (state_size > 64)
390  {
391  return ERR("failed to solve fsm: Currently only supports fsm with up to 64 state flip-flops but got " + std::to_string(state_size) + ".");
392  }
393 
394  // extract Boolean functions for each state flip-flop
395  const auto state_bfs_res = generate_state_bfs(nl, state_reg, transition_logic, true);
396  if (state_bfs_res.is_error())
397  {
398  return ERR_APPEND(state_bfs_res.get_error(), "failed to solve fsm: unable to generate Boolean functions for state.");
399  }
400  const std::vector<std::pair<Net*, BooleanFunction>> state_bfs = state_bfs_res.get();
401 
402  BooleanFunction prev_state_vec = BooleanFunctionNetDecorator(*(state_bfs.front().first)).get_boolean_variable();
403  BooleanFunction next_state_vec = state_bfs.front().second;
404  for (u32 i = 1; i < state_reg.size(); i++)
405  {
406  // bitvector representing the previous state
407  prev_state_vec = BooleanFunction::Concat(BooleanFunctionNetDecorator(*(state_bfs.at(i).first)).get_boolean_variable(), std::move(prev_state_vec), i + 1).get();
408 
409  // bitvector including all the functions to calculate the next state
410  next_state_vec = BooleanFunction::Concat(state_bfs.at(i).second.clone(), std::move(next_state_vec), i + 1).get();
411  }
412 
413  // generate initial state
414  u64 initial_state_num = 0;
415  if (!initial_state.empty())
416  {
417  for (const auto& gate : state_reg)
418  {
419  if (initial_state.find(gate) == initial_state.end())
420  {
421  return ERR("failed to solve fsm: Unable to find intial value for gate " + std::to_string(gate->get_id()) + " in the provided initial state map.");
422  }
423 
424  initial_state_num = initial_state_num << 1;
425  initial_state_num += initial_state.at(gate);
426  }
427  }
428 
429  // generate all transitions that are reachable from the inital state.
430  std::map<u64, std::set<u64>> all_transitions;
431 
432  std::deque<u64> q;
433  std::unordered_set<u64> visited;
434 
435  q.push_back(initial_state_num);
436 
437  while (!q.empty())
438  {
439  std::vector<u64> successor_states;
440 
441  u64 n = q.front();
442  q.pop_front();
443 
444  if (visited.find(n) != visited.end())
445  {
446  continue;
447  }
448  visited.insert(n);
449 
450  // generate new transitions and add them to the queue
451  SMT::Solver s;
452 
453  // set prev_state_vec to starting state
454  s = s.with_constraint(SMT::Constraint{prev_state_vec.clone(), BooleanFunction::Const(n, state_size)});
455 
456  while (true)
457  {
458  if (auto res = s.query(SMT::QueryConfig().with_model_generation().with_timeout(timeout)); res.is_error())
459  {
460  return ERR_APPEND(res.get_error(), "failed to solve fsm: failed to querry SMT solver for state " + std::to_string(n) + ".");
461  }
462  else
463  {
464  auto s_res = res.get();
465 
466  if (s_res.is_unsat())
467  {
468  break;
469  }
470 
471  if (s_res.is_unknown())
472  {
473  return ERR("failed to solve fsm: received an unknown solver result for state " + std::to_string(n) + ".");
474  }
475 
476  auto m = s_res.model.value();
477  auto suc = m.evaluate(next_state_vec).get();
478  auto suc_num = 0;
479 
480  // a constant (numeral) successor state
481  if (suc.is_constant())
482  {
483  suc_num = suc.get_constant_value_u64().get();
484  }
485  // a successor state that includes boolean functions (for example in form of input variables)
486  else
487  {
488  // 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
489  std::unordered_map<std::string, std::vector<BooleanFunction::Value>> zero_mapping;
490  for (const auto& var : suc.get_variable_names())
491  {
492  zero_mapping.insert({var, {BooleanFunction::Value::ZERO}});
493  }
494 
495  if (auto eval_res = suc.evaluate(zero_mapping); eval_res.is_error())
496  {
497  return ERR_APPEND(eval_res.get_error(), "failed to solve fsm: could not evaluate successor state to constant.");
498  }
499  else
500  {
501  suc_num = BooleanFunction::to_u64(eval_res.get()).get();
502  }
503  }
504 
505  q.push_back(suc_num);
506  all_transitions[n].insert(suc_num);
507  s = s.with_constraint(SMT::Constraint(BooleanFunction::Not(BooleanFunction::Eq(next_state_vec.clone(), BooleanFunction::Const(suc_num, suc.size()), 1).get(), 1).get()));
508  }
509  }
510  }
511 
512  const auto conditional_transitions = generate_conditional_transitions(state_bfs, all_transitions).get();
513 
514  /* DEBUG PRINTING */
515  for (const auto& [org, successors] : conditional_transitions)
516  {
517  std::cout << org << ": " << std::endl;
518  for (const auto& [suc, condition] : successors)
519  {
520  std::cout << "\t" << suc << ": " << condition.to_string() << std::endl;
521  }
522  }
523  /* END DEBUG PRINTING */
524 
525  if (auto graph_res = generate_dot_graph(state_reg, conditional_transitions, graph_path); graph_res.is_error())
526  {
527  return ERR_APPEND(graph_res.get_error(), "failed to solve fsm: unable to generate dot graph.");
528  }
529 
530  return OK(conditional_transitions);
531  }
532 
533  Result<std::string> generate_dot_graph(const std::vector<Gate*>& state_reg,
534  const std::map<u64, std::map<u64, BooleanFunction>>& transitions,
535  const std::filesystem::path& graph_path,
536  const u32 max_condition_length,
537  const u32 base)
538  {
539  std::string graph_str = "digraph {\n";
540 
541  for (const auto& [org, successors] : transitions)
542  {
543  for (const auto& [suc, cond] : successors)
544  {
545  std::string start_name;
546  std::string end_name;
547 
548  switch (base)
549  {
550  case 2:
551  start_name = std::bitset<64>(org).to_string().substr(64 - state_reg.size(), 64);
552  end_name = std::bitset<64>(suc).to_string().substr(64 - state_reg.size(), 64);
553  break;
554  case 10:
555  start_name = std::to_string(org);
556  end_name = std::to_string(suc);
557  break;
558  default:
559  return ERR("failed to generate DOT graph: base " + std::to_string(base) + "not implemented.");
560  }
561 
562  graph_str +=
563  start_name + " -> " + end_name + "[label=\"" + cond.to_string().substr(0, max_condition_length) + "\", weight=\"" + cond.to_string().substr(0, max_condition_length) + "\"];\n";
564  ;
565  }
566  }
567 
568  graph_str += "}";
569 
570  // write to file
571  if (!graph_path.empty())
572  {
573  std::ofstream ofs(graph_path);
574  if (!ofs.is_open())
575  {
576  return ERR("failed to generate DOT graph: could not open file '" + graph_path.string() + "' for writing.");
577  }
578  ofs << graph_str;
579  ofs.close();
580  }
581 
582  return OK(graph_str);
583  }
584  } // namespace solve_fsm
585 } // namespace hal
static Result< BooleanFunction > Eq(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Concat(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction clone() const
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)
BooleanFunction substitute(const std::string &old_variable_name, const std::string &new_variable_name) const
static bool is_class_of(const GateTypeComponent *component)
Definition: net.h:58
Result< SolverResult > query(const QueryConfig &config=QueryConfig()) const
Definition: solver.cpp:402
Solver & with_constraint(const Constraint &constraint)
Definition: solver.cpp:356
static bool is_class_of(const GateTypeComponent *component)
uint64_t u64
Definition: defines.h:42
#define ERR(message)
Definition: result.h:53
#define OK(...)
Definition: result.h:49
#define ERR_APPEND(prev_error, message)
Definition: result.h:57
Result< std::string > generate_dot_graph(const std::vector< Gate * > &state_reg, const std::map< u64, std::map< u64, BooleanFunction >> &transitions, const std::filesystem::path &graph_path="", const u32 max_condition_length=128, const u32 base=10)
Definition: solve_fsm.cpp:533
Result< std::map< u64, std::map< u64, BooleanFunction > > > solve_fsm(Netlist *nl, const std::vector< Gate * > &state_reg, const std::vector< Gate * > &transition_logic, const std::map< Gate *, bool > &initial_state={}, const std::filesystem::path &graph_path="", const u32 timeout=600000)
Definition: solve_fsm.cpp:381
Result< std::map< u64, std::map< u64, BooleanFunction > > > solve_fsm_brute_force(Netlist *nl, const std::vector< Gate * > &state_reg, const std::vector< Gate * > &transition_logic, const std::filesystem::path &graph_path="")
Definition: solve_fsm.cpp:285
CORE_API std::vector< T > to_vector(const Container< T, Args... > &container)
Definition: utils.h:513
string graph_path
Definition: test_plugin.py:29
dictionary initial_state
Definition: test_plugin.py:28
n
Definition: test.py:6
quint32 u32
This file contains functions to generate the state transition graph of a given FSM.
QueryConfig & with_model_generation()
Definition: types.cpp:113
QueryConfig & with_timeout(u64 seconds)
Definition: types.cpp:125