1.. _smf:
2
3State Machine Framework
4#######################
5
6.. highlight:: c
7
8Overview
9========
10
11The State Machine Framework (SMF) is an application agnostic framework that
12provides an easy way for developers to integrate state machines into their
13application. The framework can be added to any project by enabling the
14:kconfig:option:`CONFIG_SMF` option.
15
16State Creation
17==============
18
19A state is represented by three functions, where one function implements the
20Entry actions, another function implements the Run actions, and the last
21function implements the Exit actions. The prototype for these functions is as
22follows: ``void funct(void *obj)``, where the ``obj`` parameter is a user
23defined structure that has the state machine context, :c:struct:`smf_ctx`, as
24its first member. For example::
25
26   struct user_object {
27      struct smf_ctx ctx;
28      /* All User Defined Data Follows */
29   };
30
31The :c:struct:`smf_ctx` member must be first because the state machine
32framework's functions casts the user defined object to the :c:struct:`smf_ctx`
33type with the :c:macro:`SMF_CTX` macro.
34
35For example instead of doing this ``(struct smf_ctx *)&user_obj``, you could
36use ``SMF_CTX(&user_obj)``.
37
38By default, a state can have no ancestor states, resulting in a flat state
39machine. But to enable the creation of a hierarchical state machine, the
40:kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` option must be enabled.
41
42By default, the hierarchical state machines do not support initial transitions
43to child states on entering a superstate. To enable them the
44:kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` option must be enabled.
45
46The following macro can be used for easy state creation:
47
48* :c:macro:`SMF_CREATE_STATE` Create a state
49
50State Machine Creation
51======================
52
53A state machine is created by defining a table of states that's indexed by an
54enum. For example, the following creates three flat states::
55
56   enum demo_state { S0, S1, S2 };
57
58   const struct smf_state demo_states[] = {
59      [S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit, NULL, NULL),
60      [S1] = SMF_CREATE_STATE(s1_entry, s1_run, s1_exit, NULL, NULL),
61      [S2] = SMF_CREATE_STATE(s2_entry, s2_run, s2_exit, NULL, NULL)
62   };
63
64And this example creates three hierarchical states::
65
66   enum demo_state { S0, S1, S2 };
67
68   const struct smf_state demo_states[] = {
69      [S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit, parent_s0, NULL),
70      [S1] = SMF_CREATE_STATE(s1_entry, s1_run, s1_exit, parent_s12, NULL),
71      [S2] = SMF_CREATE_STATE(s2_entry, s2_run, s2_exit, parent_s12, NULL)
72   };
73
74
75This example creates three hierarchical states with an initial transition
76from parent state S0 to child state S2::
77
78   enum demo_state { S0, S1, S2 };
79
80   /* Forward declaration of state table */
81   const struct smf_state demo_states[];
82
83   const struct smf_state demo_states[] = {
84      [S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit, NULL, demo_states[S2]),
85      [S1] = SMF_CREATE_STATE(s1_entry, s1_run, s1_exit, demo_states[S0], NULL),
86      [S2] = SMF_CREATE_STATE(s2_entry, s2_run, s2_exit, demo_states[S0], NULL)
87   };
88
89To set the initial state, the :c:func:`smf_set_initial` function should be
90called.
91
92To transition from one state to another, the :c:func:`smf_set_state`
93function is used.
94
95.. note:: If :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` is not set,
96   :c:func:`smf_set_initial` and :c:func:`smf_set_state` function should
97   not be passed a parent state as the parent state does not know which
98   child state to transition to. Transitioning to a parent state is OK
99   if an initial transition to a child state is defined. A well-formed
100   HSM should have initial transitions defined for all parent states.
101
102.. note:: While the state machine is running, :c:func:`smf_set_state` should
103   only be called from the Entry or Run function. Calling
104   :c:func:`smf_set_state` from Exit functions will generate a warning in the
105   log and no transition will occur.
106
107State Machine Execution
108=======================
109
110To run the state machine, the :c:func:`smf_run_state` function should be
111called in some application dependent way. An application should cease calling
112smf_run_state if it returns a non-zero value.
113
114Preventing Parent Run Actions
115=============================
116
117Calling :c:func:`smf_set_handled` prevents calling the run action of parent
118states. It is not required to call :c:func:`smf_set_handled` if the state
119calls :c:func:`smf_set_state`.
120
121State Machine Termination
122=========================
123
124To terminate the state machine, the :c:func:`smf_set_terminate` function
125should be called. It can be called from the entry, run, or exit actions. The
126function takes a non-zero user defined value that will be returned by the
127:c:func:`smf_run_state` function.
128
129UML State Machines
130==================
131
132SMF follows UML hierarchical state machine rules for transitions i.e., the
133entry and exit actions of the least common ancestor are not executed on
134transition, unless said transition is a transition to self.
135
136The UML Specification for StateMachines may be found in chapter 14 of the UML
137specification available here: https://www.omg.org/spec/UML/
138
139SMF breaks from UML rules in:
140
1411. Executing the actions associated with the transition within the context
142   of the source state, rather than after the exit actions are performed.
1432. Only allowing external transitions to self, not to sub-states. A transition
144   from a superstate to a child state is treated as a local transition.
1453. Prohibiting transitions using :c:func:`smf_set_state` in exit actions.
146
147SMF also does not provide any pseudostates except the Initial Pseudostate.
148Terminate pseudostates can be modelled by calling  :c:func:`smf_set_terminate`
149from the entry action of a 'terminate' state. Orthogonal regions are modelled
150by calling :c:func:`smf_run_state` for each region.
151
152State Machine Examples
153======================
154
155Flat State Machine Example
156**************************
157
158This example turns the following state diagram into code using the SMF, where
159the initial state is S0.
160
161.. graphviz::
162   :caption: Flat state machine diagram
163
164   digraph smf_flat {
165      node [style=rounded];
166      init [shape = point];
167      STATE_S0 [shape = box];
168      STATE_S1 [shape = box];
169      STATE_S2 [shape = box];
170
171      init -> STATE_S0;
172      STATE_S0 -> STATE_S1;
173      STATE_S1 -> STATE_S2;
174      STATE_S2 -> STATE_S0;
175   }
176
177Code::
178
179	#include <zephyr/smf.h>
180
181	/* Forward declaration of state table */
182	static const struct smf_state demo_states[];
183
184	/* List of demo states */
185	enum demo_state { S0, S1, S2 };
186
187	/* User defined object */
188	struct s_object {
189		/* This must be first */
190		struct smf_ctx ctx;
191
192		/* Other state specific data add here */
193	} s_obj;
194
195	/* State S0 */
196	static void s0_entry(void *o)
197	{
198		/* Do something */
199	}
200	static void s0_run(void *o)
201	{
202		smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
203	}
204	static void s0_exit(void *o)
205	{
206		/* Do something */
207	}
208
209	/* State S1 */
210	static void s1_run(void *o)
211	{
212		smf_set_state(SMF_CTX(&s_obj), &demo_states[S2]);
213	}
214	static void s1_exit(void *o)
215	{
216		/* Do something */
217	}
218
219	/* State S2 */
220	static void s2_entry(void *o)
221	{
222		/* Do something */
223	}
224	static void s2_run(void *o)
225	{
226		smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
227	}
228
229	/* Populate state table */
230	static const struct smf_state demo_states[] = {
231		[S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit, NULL, NULL),
232		/* State S1 does not have an entry action */
233		[S1] = SMF_CREATE_STATE(NULL, s1_run, s1_exit, NULL, NULL),
234		/* State S2 does not have an exit action */
235		[S2] = SMF_CREATE_STATE(s2_entry, s2_run, NULL, NULL, NULL),
236	};
237
238	int main(void)
239	{
240		int32_t ret;
241
242		/* Set initial state */
243		smf_set_initial(SMF_CTX(&s_obj), &demo_states[S0]);
244
245		/* Run the state machine */
246		while(1) {
247			/* State machine terminates if a non-zero value is returned */
248			ret = smf_run_state(SMF_CTX(&s_obj));
249			if (ret) {
250				/* handle return code and terminate state machine */
251				break;
252			}
253			k_msleep(1000);
254		}
255	}
256
257Hierarchical State Machine Example
258**********************************
259
260This example turns the following state diagram into code using the SMF, where
261S0 and S1 share a parent state and S0 is the initial state.
262
263
264.. graphviz::
265   :caption: Hierarchical state machine diagram
266
267   digraph smf_hierarchical {
268      node [style = rounded];
269      init [shape = point];
270      STATE_S0 [shape = box];
271      STATE_S1 [shape = box];
272      STATE_S2 [shape = box];
273
274      subgraph cluster_0 {
275         label = "PARENT";
276         style = rounded;
277         STATE_S0 -> STATE_S1;
278      }
279
280      init -> STATE_S0;
281      STATE_S1 -> STATE_S2;
282      STATE_S2 -> STATE_S0;
283   }
284
285Code::
286
287	#include <zephyr/smf.h>
288
289	/* Forward declaration of state table */
290	static const struct smf_state demo_states[];
291
292	/* List of demo states */
293	enum demo_state { PARENT, S0, S1, S2 };
294
295	/* User defined object */
296	struct s_object {
297		/* This must be first */
298		struct smf_ctx ctx;
299
300		/* Other state specific data add here */
301	} s_obj;
302
303	/* Parent State */
304	static void parent_entry(void *o)
305	{
306		/* Do something */
307	}
308	static void parent_exit(void *o)
309	{
310		/* Do something */
311	}
312
313	/* State S0 */
314	static void s0_run(void *o)
315	{
316		smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
317	}
318
319	/* State S1 */
320	static void s1_run(void *o)
321	{
322		smf_set_state(SMF_CTX(&s_obj), &demo_states[S2]);
323	}
324
325	/* State S2 */
326	static void s2_run(void *o)
327	{
328		smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
329	}
330
331	/* Populate state table */
332	static const struct smf_state demo_states[] = {
333		/* Parent state does not have a run action */
334		[PARENT] = SMF_CREATE_STATE(parent_entry, NULL, parent_exit, NULL, NULL),
335		/* Child states do not have entry or exit actions */
336		[S0] = SMF_CREATE_STATE(NULL, s0_run, NULL, &demo_states[PARENT], NULL),
337		[S1] = SMF_CREATE_STATE(NULL, s1_run, NULL, &demo_states[PARENT], NULL),
338		/* State S2 do not have entry or exit actions and no parent */
339		[S2] = SMF_CREATE_STATE(NULL, s2_run, NULL, NULL, NULL),
340	};
341
342	int main(void)
343	{
344		int32_t ret;
345
346		/* Set initial state */
347		smf_set_initial(SMF_CTX(&s_obj), &demo_states[S0]);
348
349		/* Run the state machine */
350		while(1) {
351			/* State machine terminates if a non-zero value is returned */
352			ret = smf_run_state(SMF_CTX(&s_obj));
353			if (ret) {
354				/* handle return code and terminate state machine */
355				break;
356			}
357			k_msleep(1000);
358		}
359	}
360
361When designing hierarchical state machines, the following should be considered:
362 - Ancestor entry actions are executed before the sibling entry actions. For
363   example, the parent_entry function is called before the s0_entry function.
364 - Transitioning from one sibling to another with a shared ancestry does not
365   re-execute the ancestor\'s entry action or execute the exit action.
366   For example, the parent_entry function is not called when transitioning
367   from S0 to S1, nor is the parent_exit function called.
368 - Ancestor exit actions are executed after the exit action of the current
369   state. For example, the s1_exit function is called before the parent_exit
370   function is called.
371 - The parent_run function only executes if the child_run function does not
372   call either :c:func:`smf_set_state` or :c:func:`smf_set_handled`.
373
374Event Driven State Machine Example
375**********************************
376
377Events are not explicitly part of the State Machine Framework but an event driven
378state machine can be implemented using Zephyr :ref:`events`.
379
380.. graphviz::
381   :caption: Event driven state machine diagram
382
383   digraph smf_flat {
384      node [style=rounded];
385      init [shape = point];
386      STATE_S0 [shape = box];
387      STATE_S1 [shape = box];
388
389      init -> STATE_S0;
390      STATE_S0 -> STATE_S1 [label = "BTN EVENT"];
391      STATE_S1 -> STATE_S0 [label = "BTN EVENT"];
392   }
393
394Code::
395
396	#include <zephyr/kernel.h>
397	#include <zephyr/drivers/gpio.h>
398	#include <zephyr/smf.h>
399
400	#define SW0_NODE        DT_ALIAS(sw0)
401
402	/* List of events */
403	#define EVENT_BTN_PRESS BIT(0)
404
405	static const struct gpio_dt_spec button =
406		GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
407
408	static struct gpio_callback button_cb_data;
409
410	/* Forward declaration of state table */
411	static const struct smf_state demo_states[];
412
413	/* List of demo states */
414	enum demo_state { S0, S1 };
415
416	/* User defined object */
417	struct s_object {
418		/* This must be first */
419		struct smf_ctx ctx;
420
421		/* Events */
422		struct k_event smf_event;
423		int32_t events;
424
425		/* Other state specific data add here */
426	} s_obj;
427
428	/* State S0 */
429	static void s0_entry(void *o)
430	{
431		printk("STATE0\n");
432	}
433
434	static void s0_run(void *o)
435	{
436		struct s_object *s = (struct s_object *)o;
437
438		/* Change states on Button Press Event */
439		if (s->events & EVENT_BTN_PRESS) {
440			smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
441		}
442	}
443
444	/* State S1 */
445	static void s1_entry(void *o)
446	{
447		printk("STATE1\n");
448	}
449
450	static void s1_run(void *o)
451	{
452		struct s_object *s = (struct s_object *)o;
453
454		/* Change states on Button Press Event */
455		if (s->events & EVENT_BTN_PRESS) {
456			smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
457		}
458	}
459
460	/* Populate state table */
461	static const struct smf_state demo_states[] = {
462		[S0] = SMF_CREATE_STATE(s0_entry, s0_run, NULL, NULL, NULL),
463		[S1] = SMF_CREATE_STATE(s1_entry, s1_run, NULL, NULL, NULL),
464	};
465
466	void button_pressed(const struct device *dev,
467			struct gpio_callback *cb, uint32_t pins)
468	{
469		/* Generate Button Press Event */
470		k_event_post(&s_obj.smf_event, EVENT_BTN_PRESS);
471	}
472
473	int main(void)
474	{
475		int ret;
476
477		if (!gpio_is_ready_dt(&button)) {
478			printk("Error: button device %s is not ready\n",
479				button.port->name);
480			return;
481		}
482
483		ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
484		if (ret != 0) {
485			printk("Error %d: failed to configure %s pin %d\n",
486				ret, button.port->name, button.pin);
487			return;
488		}
489
490		ret = gpio_pin_interrupt_configure_dt(&button,
491			GPIO_INT_EDGE_TO_ACTIVE);
492		if (ret != 0) {
493			printk("Error %d: failed to configure interrupt on %s pin %d\n",
494				ret, button.port->name, button.pin);
495			return;
496		}
497
498		gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
499		gpio_add_callback(button.port, &button_cb_data);
500
501		/* Initialize the event */
502		k_event_init(&s_obj.smf_event);
503
504		/* Set initial state */
505		smf_set_initial(SMF_CTX(&s_obj), &demo_states[S0]);
506
507		/* Run the state machine */
508		while(1) {
509			/* Block until an event is detected */
510			s_obj.events = k_event_wait(&s_obj.smf_event,
511					EVENT_BTN_PRESS, true, K_FOREVER);
512
513			/* State machine terminates if a non-zero value is returned */
514			ret = smf_run_state(SMF_CTX(&s_obj));
515			if (ret) {
516				/* handle return code and terminate state machine */
517				break;
518			}
519		}
520	}
521
522State Machine Example With Initial Transitions And Transition To Self
523*********************************************************************
524
525:zephyr_file:`tests/lib/smf/src/test_lib_self_transition_smf.c` defines a state
526machine for testing the initial transitions and transitions to self in a parent
527state. The statechart for this test is below.
528
529
530.. graphviz::
531   :caption: Test state machine for UML State Transitions
532
533   digraph smf_hierarchical_initial {
534      compound=true;
535      node [style = rounded];
536      "smf_set_initial()" [shape=plaintext fontname=Courier];
537      ab_init_state [shape = point];
538      STATE_A [shape = box];
539      STATE_B [shape = box];
540      STATE_C [shape = box];
541      STATE_D [shape = box];
542      DC[shape=point height=0 width=0 label=<>]
543
544      subgraph cluster_root {
545         label = "ROOT";
546         style = rounded;
547
548         subgraph cluster_ab {
549            label = "PARENT_AB";
550            style = rounded;
551            ab_init_state -> STATE_A;
552            STATE_A -> STATE_B;
553         }
554
555         subgraph cluster_c {
556            label = "PARENT_C";
557            style = rounded;
558            STATE_B -> STATE_C [ltail=cluster_ab]
559         }
560
561         STATE_C -> DC [ltail=cluster_c, dir=none];
562         DC -> STATE_C [lhead=cluster_c];
563         STATE_C -> STATE_D
564      }
565
566      "smf_set_initial()" -> STATE_A [lhead=cluster_ab]
567   }
568
569
570API Reference
571=============
572
573.. doxygengroup:: smf
574