Author Topic: Use an explicit and declarative state machine in firmware  (Read 1133 times)

0 Members and 1 Guest are viewing this topic.

Offline YTusernameTopic starter

  • Regular Contributor
  • *
  • Posts: 83
  • Country: tr
    • Atadiat
Use an explicit and declarative state machine in firmware
« on: March 11, 2024, 06:54:39 am »
Hi,
I've participated in discussions in this forum regarding firmware architecture, such as this one an this one. Additionally, recently, I watched an interesting talk by C++ developer Kris Jusiak at Cpp Now 2019 titled "Rise of the State Machines."

In his presentation, he introduced a library called SML. All of this has encouraged me to create a two-part series promoting the adoption of a declarative and efficient state machine in our firmware designs. I published the first part yesterday.

Please review it and share your thoughts on the SML library. For those who enjoy written content, please check out the associated article.
« Last Edit: March 11, 2024, 06:56:42 am by YTusername »
 

Offline Doctorandus_P

  • Super Contributor
  • ***
  • Posts: 3367
  • Country: nl
Re: Use an explicit and declarative state machine in firmware
« Reply #1 on: March 11, 2024, 09:02:09 am »
A method I like is to give each state it's own separate function, and then use a function pointer to remember the current state. Each of those function states can then modify this function pointer to set it to the next state. Especially in C++ this can be nicely encapsulated in a class. The class can also hold context information, and you can declare multiple instances of the class to run multiple state machines concurrently.

I don't know if this method is mentiond in that video, but I don't have an hour and a half of patience for that.
 

Offline YTusernameTopic starter

  • Regular Contributor
  • *
  • Posts: 83
  • Country: tr
    • Atadiat
Re: Use an explicit and declarative state machine in firmware
« Reply #2 on: March 11, 2024, 10:14:54 am »
A method I like is to give each state it's own separate function, and then use a function pointer to remember the current state. Each of those function states can then modify this function pointer to set it to the next state. Especially in C++ this can be nicely encapsulated in a class. The class can also hold context information, and you can declare multiple instances of the class to run multiple state machines concurrently.

I think the closest implementation is in the `inheritance/state pattern` method (here). It is not identical to your way, but each state has a class and all inherit a main class that has a `make_unique` pointer shared between all.
 

Offline Doctorandus_P

  • Super Contributor
  • ***
  • Posts: 3367
  • Country: nl
Re: Use an explicit and declarative state machine in firmware
« Reply #3 on: March 11, 2024, 12:51:47 pm »
It's sort of like that, but I get lost in the details over there. I learned C a long time ago, and for me C++ is still a superset of C. I also do not do enough programming to be fluent in C++, and there are other... complications too.

I once used a function pointer in C for a statemachine, and that worked reasonably well. The state machine was in it's own file, and the states declared as static functions, so they were not visible outside the file. Later, when I got interested in C++ I revised that and turned it into a single class.

A problem with the programmatic approach is the lack of overview. I like the graphical view with round bubbles for the states and arrows (with conditions) for state changes. Such a diagram gives a quick overview of a whole statemachine. I once drew it in autocad (it was a long time ago) but that has the disadvantage that it complicates maintenance during revisions. The code and the picture easily get out of sync. I know there is generator software in which you can draw a state diagram and then it can compile it into code but I never used it. Such software probably has issues too...
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Re: Use an explicit and declarative state machine in firmware
« Reply #4 on: March 11, 2024, 02:11:15 pm »
A problem with the programmatic approach is the lack of overview. I like the graphical view with round bubbles for the states and arrows (with conditions) for state changes. Such a diagram gives a quick overview of a whole statemachine. I once drew it in autocad (it was a long time ago) but that has the disadvantage that it complicates maintenance during revisions. The code and the picture easily get out of sync. I know there is generator software in which you can draw a state diagram and then it can compile it into code but I never used it. Such software probably has issues too...

that would be UML!
or at least i find it much easier and faster to change one line in UML, whereas the graphic's guy and a contractor insist in using Miro. sigh. Though it supposedly now supports UML for generating charts and other it is not used
 

Offline YTusernameTopic starter

  • Regular Contributor
  • *
  • Posts: 83
  • Country: tr
    • Atadiat
Re: Use an explicit and declarative state machine in firmware
« Reply #5 on: March 11, 2024, 02:32:37 pm »
You can check the PlantUML and SML section in my article. That is exactly what you are talking about, a scripting language for UML generation. The creator of SML library included a code in one of his workshops that converts the transition table expressed in SML library to PlantUML script.
 

Online tellurium

  • Regular Contributor
  • *
  • Posts: 231
  • Country: ua
Re: Use an explicit and declarative state machine in firmware
« Reply #6 on: March 12, 2024, 06:53:57 am »
To me, C++ and simplicity are antagonists.
Found that that SML library, saw  tons of "template class ...", closed it. Unlikely to open it again.

IMO, a C code which declares states explicitly and uses a simple switch/case or if/else, makes FSM easy to write and maintain.
If the code becomes big - well, factor out big parts, and keep the FSM core small.
Example: a single-pass JSON parser that uses no heap, implemented as an FSM with clearly defined states:
https://github.com/cesanta/mongoose/blob/9f498d2eea27737bea2f8bce47107d328e881ad0/src/json.c#L129

The whole function is less than 100 LoC, so it was not even broken down into a smaller functions - no need to.

Every state has a clear transition points. For example, a ":" character transits the state to expect a JSON value:
https://github.com/cesanta/mongoose/blob/9f498d2eea27737bea2f8bce47107d328e881ad0/src/json.c#L240-L242

That's it. The only thing that I'd like to have here, is to visualise the FSM - and probably it can be done with a tool that parses the FSM code and spits out a graph.

The SML approach described in the "SML Library: Simplified Overview" chapter of your article does not resonate as "simple" to me at all.
Open source embedded network library https://mongoose.ws
TCP/IP stack + TLS1.3 + HTTP/WebSocket/MQTT in a single file
 

Offline YTusernameTopic starter

  • Regular Contributor
  • *
  • Posts: 83
  • Country: tr
    • Atadiat
Re: Use an explicit and declarative state machine in firmware
« Reply #7 on: March 12, 2024, 09:37:48 am »
To me, C++ and simplicity are antagonists.

For many firmware developers, that's true, but I would say it's mostly because we're not accustomed to the C++ style. This is an industry-wide phenomenon.  I agree that, at some points understanding a complex  C++ code can be very hard. It is very rare to have difficulties understanding C code. I can not judge as my main background is C. on the other hand, We cannot overlook the success of many C++ firmware projects like Mbed-OS, which is implemented entirely in C++. The debate about C vs. C++ will never end, I know. I still consider myself a C firmware developer rather than C++. But after attending several talks from Cpp Now event, I'm considering the C++.
 
Found that that SML library, saw  tons of "template class ...", closed it. Unlikely to open it again.

Indeed, it can be daunting, I cannot argue with that. However, I don't believe there is another way to create a similar library without using such intertwined template classes. I mean, if the library developers know what they're doing, performance is validated, and, most importantly, debugging is supported, then having complexity in its implementation isn't necessarily a bad thing. After all, we already place trust in many libraries and dependencies.

IMO, a C code which declares states explicitly and uses a simple switch/case or if/else, makes FSM easy to write and maintain.
If the code becomes big - well, factor out big parts, and keep the FSM core small.
Example: a single-pass JSON parser that uses no heap, implemented as an FSM with clearly defined states:
https://github.com/cesanta/mongoose/blob/9f498d2eea27737bea2f8bce47107d328e881ad0/src/json.c#L129

The whole function is less than 100 LoC, so it was not even broken down into a smaller functions - no need to.

Every state has a clear transition points. For example, a ":" character transits the state to expect a JSON value:
https://github.com/cesanta/mongoose/blob/9f498d2eea27737bea2f8bce47107d328e881ad0/src/json.c#L240-L242
Nice example. Of course, SML is not a one-size-fits-all solution.


That's it. The only thing that I'd like to have here, is to visualise the FSM - and probably it can be done with a tool that parses the FSM code and spits out a graph.
The cool thing about the SML library is that you have a clear transition table in one place, which you can later dump using code to convert it into a graph (mentioned the dumping code in the article).

The SML approach described in the "SML Library: Simplified Overview" chapter of your article does not resonate as "simple" to me at all.
Thanks for the feedback.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf