Home > On-Demand Archives > Q&A Sessions >

Live Q&A - Modern Embedded Programming with Hierarchical State Machines and Active Objects

Miro Samek - Watch Now - EOC 2021 - Duration: 19:42

M↓ MARKDOWN HELP
italicssurround text with
*asterisks*
boldsurround text with
**two asterisks**
hyperlink
[hyperlink](https://example.com)
or just a bare URL
code
surround text with
`backticks`
strikethroughsurround text with
~~two tilde characters~~
quote
prefix with
>

MiroSpeaker
Score: 0 | 3 years ago | no reply

From Steve Wheeler : Do the QP frameworks generate timing/event diagrams such as the one you showed?

QP contains software tracing facility called QP/Spy. This can output all sorts of data about the system execution, because an active object framework like QP knows so much more about the application. For example, QP framework knows about state machine execution: transitions, state entry/exit, etc. This is much more than software tracing of an RTOS kernel.

So, from this tracing data, the QSPY host application can generate all sorts of outputs, including sequence diagrams. You can also generate output for MATLAB, to analyze your traces in very sophisticated ways.

MiroSpeaker
Score: 0 | 3 years ago | no reply

From Davy Baker : What is the minimum size of flash and ram you have seen work worth Active Objects ?

The code size (ROM) of QP RTEF can be about the same as bare-bones RTOS kernel (around 4KB on ARM Cortex-M). This is assuming that QP uses one of the built-in kernels (cooperative QV kernel, preemptive, non-blocking QK kernel, or the dual-mode QXK kernel).

But when it comes to RAM consumption, QP requires less RAM than any traditional RTOS, because the built-in QV and QK kernels use only single stack for all AOs and interrupts. For example, it makes sense to use QP with as little as 1KB or RAM total.

MiroSpeaker
Score: 0 | 3 years ago | no reply

From patelk5 : Are there any concurrency issues to be cognizant of even if Active Objects are used?

The traditional concurrency issues are all related to sharing of resources, so if you don't share anything you should not experience any of them.

The tradeoff with Active Objects is that you now use event passing. The good news is that event passing is handled by the framework (like QP) in a thread-safe manner. But still, there are limitations as to what you can and cannot do with these events. For example, you are not allowed to write to any received event. You are also not allowed to keep using an event after the RTC step. And there are some other restrictions.

Also, additional tradeoff is that queuing of events can sometimes lead to unexpected events being present in your queue, or events might get queued in unexpected order. For example, you might unsubscribe from an event and so you might not expect this event anymore. But the event might be already in your event queue, so it arrives seemingly after you've unsubscribed. Also, due to preemption some AOs might run and post events ahead of other AOs. So this can lead to re-ordering of events.

But all these issues are generally easier to deal with than the classic race conditions or data corruption. The main reason being that classic concurrency hazards do the damage before you can know about it. Here no damage is done until you process the event, so you can design your state machines to deal with the issues.

MiroSpeaker
Score: 0 | 4 years ago | no reply

I have just added several answers to questions asked during the live Q&A session. Please check out that discussion.

MiroSpeaker
Score: 0 | 4 years ago | no reply

From Radu Pralea : This approach (Active Objects, State Machines) seems a very elegant way of expressing the problem in a more optimal and robust way compared to a threading approach (eliminating sharing and (some) timing problems and overheads).
However,:

  • it seems (at least at a first glance) a less natural way of thinking about the problem domain (more indirect, i.e. designing the sequence diagram with its constraints (no sharing), compared to a closer to the problem domain modeling approach)

Whether or not a method seems "natural" depends entirely on familiarity. To people familiar with event-driven Active Objects it's exactly the other way around. The traditional "shared-state concurrency and blocking" seem completely unnatural and backwards. For example, Active Objects are closer to the problem domain than blocking threads and mutual exclusion. I was exactly hoping to convey this in my presentation, where the "Tunnel", "Ship" and "Missile" active objects modeled directly the behavior of their real-life counterparts.

  • it seems that the sequence diagram doesn't scale very well (I expect it to become less manageable with increasing size/number of concepts, compared to the task oriented design)

Sure, a single sequence diagram can hold only so much information. But who said that you can have only one sequence diagram? In fact, in any non-trivial project you will have many sequence diagrams, typically associated with use cases. Use-cases are in themselves a very useful concept, and I highly recommend to read about them.

Regarding "task oriented design", perhaps you could point me to some resources describing what that is. Do you mean flowcharts describing the sequential code? I know that Jean Labrosse introduced some quite expressive diagrams in his RTOS books, where he depicted semaphores, queues, mutexes, etc. But these are by no means standard, universally understood types of diagrams. In contrast: sequence diagrams and state diagrams (UML statechars) are standardized and have universally understood meaning.

  • it seems that it's a less agile way of partitioning the problem: it would work fine for a reasonably sized problem that's very well defined beforehand, but it would require very inconvenient redesigns as the requirements keep coming and/or changing

It is exactly the other way around! Event-driven components are much more extensible than traditional blocking threads, because you can add new events and new event sequences very easily. In contrast, traditional sequential RTOS threads block to wait for events, so adding new events is hard because the system is unresponsive to them. This means that you have to keep adding new threads, just to handle new events. I showed this in my last year's presentation, where I merged two threads into one active object, exactly because the AO was extensible.

I also blogged about it in my post "RTOS, TDD and the ?O? in the S-O-L-I-D rules"

MiroSpeaker
Score: 0 | 4 years ago | no reply

From afwaanquadri : How do you suggest an AO to handle a blocking call? such as a HTTP request.

AO should not block internally, period. So if you have an inherently blocking API, you need to use a regular blocking thread to handle that. Please note that event-driven AOs and traditional blocking threads can be used in the same system and they can cooperate. But what you should never do is to mix event-driven and sequential programming styles within a single thread.

Having said that, HTTP can be handled in a pure event-driven fashion without any blocking. There is an App Note "QP? and lwIP TCP/IP Stack", which shows an HTTP server running inside a non-blocking AO. There is also a companion video showing how this works.

MiroSpeaker
Score: 0 | 4 years ago | no reply

From Alex Burka : Why not use C++?

This presentation used QP/C RTEF and C as the primary language, because 70+% of embedded software is written in C, and it is typically not quite clear to C programmers how to use object-oriented design patterns like "Active Objects".

But QP/C++ RTEF is also available, where it is very clear how to implement object-orientation.

MiroSpeaker
Score: 0 | 4 years ago | no reply

From patelk5 : Is there any advantage of using a sequential code flow (like RTOS or foreground/background) over event-driven code flow (RTEF)? It seems like event-driven is superior in almost every scenario?

Sequential solution is good if the problem is sequential as well. By this I mean that the system needs to handle only a very limited number of event sequences. In that case hard-coding them in sequential code is the simplest.

Trouble is that most real life problems are NOT sequential and the system must handle many event sequences. But typically you don't know it, until you start building your system. This is very insidious, because initially every problem looks sequential, so you start that way. But inevitably you discover some other legitimate event sequences that need to be handled as well, so your sequential code degenerates...

So, yes, I agree that most of the time it is better to assume event-driven architecture that is exactly prepared to handle multiple event sequences.

MiroSpeaker
Score: 0 | 4 years ago | no reply

From Tim Michals : Is there a Linux version of all the tools and examples?

Yes, Linux is supported as both the target to run QP and as development host.

As target, two QP ports to Linux (POSIX) are available in the <qpc|qpcpp>/ports/posix and <qpc|qpcpp>/ports/posix-qv directories. There is an AppNote "QP and POSIX" that describes how QP can work with P-threads and within just one thread. Examples are provided in the <qpc|qpcpp>/examples/workstation directory. There are also videos showing how to run QP on Raspberry Pi (embedded Linux).

Linux as development host is supported as well. Specifically there is a Linux version of the QM modeling tool and other tools (e.g., QSPY) also run on Linux.

MarkW
Score: 1 | 4 years ago | 1 reply

Great implementation and really awesome design paradigm that I would like to try get started at the company I work for (which is "unfortunately" very large with very 'set' ways about current designs. But perhaps a new project will come along where it can be introduced.
I am thinking about how this can be applied where there is a object that reads/writes data to NvM. Other tasks might need to read/write, so eventually you will have a kind of 'blocking'. I can think of a task that needs to read data and then perform an action, so it posts a 'read event' to an NvM object, and then subscribes to a "READ_DONE" event. Is this the right way of thinking? And then the NvM object would have a state machine that is serviced on a tick (since it might need to wait for hardware)?

MiroSpeaker
Score: 0 | 4 years ago | 1 reply

If you encapsulate the NvM inside a dedicated AO, you won't have blocking of other AOs, because you have queuing of events. But you must be careful to adequately size the event queue of the NvM-AO and to also adequately size the event pool from which you'll allocate the events.

You typically don't need to have "READ_DONE" event, just as empty acknowledgements. Instead, you will have NVM_DATA event, which will carry the requested data inside.

Regarding the internal structure of the NvM-AO, yes, it will have a state machine, or maybe two state machines one for reading and an "orthogonal" component state machine for writing (similar to the Mine components of the Tunnel-AO in the game). If the NvM-AO needs to wait for some timeouts, it can use Time-Events (please watch my last year's presentation and the little FreeACT framework).

In the end, having a dedicated NvM-AO has numerous benefits compared to the traditional sharing of the NvM and protecting it with a mutex.

  • NvM-AO will resolve any potential conflicts. For example, two AOs trying to write to the same location at the same time will be naturally sequenced by the event queue of the NvM-AO and RTC (run-to-completion) event processing.

  • NvM-AO can use the "Deferred Event" state pattern to appear always responsive to events.

  • NvM-AO can also much easier implement any wear-leveling algorithms than an alternative when access to NvM is "distributed" among many threads.

  • NvM-AO with clean event-driven interface is more portable if the underlying NvM interface or technology were to change.

  • Finally, note that a dedicated NvM-AO will not slow down the access to the NvM, because the bottleneck is not the CPU. Rather, the bottleneck is the NvM interface and potentially the erase operations when writing.

Of course, this is just your example of NvM management. But similar arguments can be made about most other resources--it usually is better and cleaner to have a "manager" of "broker" to resolve conflicts, streamline access, etc.

MarkW
Score: 0 | 3 years ago | no reply

Thanks for the detailed response!
I can definitely see the advantages of this event driven AO paradigm, and will definitely try it when the opportunity arises in my professional career (also in a personal project if I get time for that)

malreverse
Score: 1 | 4 years ago | 1 reply

Are there good SDD's (software design documents) out there that are available for download? Thank you.

MiroSpeaker
Score: 1 | 4 years ago | no reply

The design documentation for customer's projects done with QP is typically proprietary, so I can't point you there. But it's a good idea to provide an example of an "open source" SDD even for a small application like the "Fly 'n' Shoot" game. I will put it on my TODO list and try to deliver this in the upcoming QP releases.

Harri
Score: 1 | 4 years ago | no reply

Thank you for the presentation!
I definitely need to try this pattern both in embedded and Windows ports. Also glad to see Gecko boards represented :)

KushP
Score: 1 | 4 years ago | 1 reply

Are there any concurrency issues to be cognizant of even if Active Objects are used? I presume race conditions could still happen depending on order events are received.

MiroSpeaker
Score: 0 | 4 years ago | 1 reply

If you really avoid sharing of anything (except events) among active objects, then the classic race conditions and data corruption should NOT happen. They are replaced with "event-races" (due to active object threads preempting each other), which can lead to "unexpected" reordering of events . But event ordering issues are much more tractable than race conditions and corruption, where the damage is already done. In contrast, with re-ordered events no damage is done yet. But yes, you need to design your state machines to handle such unobvious event sequences. With some experience you will develop a sense when such "event races" can happen and when you need to prepare your state machines to handle them. I provide some examples of this in my PSiCC2 book (the section about Time Events).

KushP
Score: 0 | 4 years ago | no reply

Thank you Dr. Samek. I will have to complete your book.

Dan6077
Score: 2 | 4 years ago | 1 reply

I really like the concept of Active Actors
It looks a little like the code that one would create in Visual Basic and attach to a Button Click

MiroSpeaker
Score: 1 | 4 years ago | no reply

Visual Basic is an example of an event-driven architecture, but active objects, at least in the QP framework, have state machines. This is a much more powerful mechanism than simple "event-handlers" from Visual Basic, because a state machine remembers the context (as the current state) from one event to the next. In my last year's talk I exactly used the Visual Basic calculator example to illustrate the problems with simple "event-handlers". I'd highly recommend that you watch that presentation, because it also introduced state machines.

Javi
Score: 0 | 4 years ago | 1 reply

Thank Miro, great talk, amazing job!
just a quick question about event passing. How are they sent/receive? Do you use a queue? a mailbox?
This is a new paradigm for me and I am impressed how nice and clean it is! looking forward to try it out!
Thanks again for sharing
javi

MiroSpeaker
Score: 2 | 4 years ago | no reply

Yes, there is an event queue in each active object. If you are interested in the details, please watch my last year's presentation. Among others, I presented there a minimal, but fully functional implementation of the "Active Object" design pattern on top of FreeRTOS. That little framework was called "FreeACT" (I mentioned it in this talk) with the code available on GitHub (see https://github.com/QuantumLeaps/ ).

mgaron
Score: 2 | 4 years ago | 1 reply

I'm already sold and using the QP framework, yet attending these sessions year after year, still amazed by the simplicity and elegance of it. Whenever possible, I avoid going back to traditional RTOSes.
I still learned a 2-3 things from this presentation. Thanks a lot and keep up the good work.

MiroSpeaker
Score: 1 | 4 years ago | no reply

You seem to be well informed about the method, but I'm glad that you were still able to find something new in this presentation. The concepts of active objects and state machines can be implemented in many different ways. But as usual, the devil is in the detail, so to be really useful for deeply embedded MCUs the abstractions have to be implemented efficiently and the supported features need to be chosen very carefully.

KushP
Score: 2 | 4 years ago | 1 reply

Is there any advantage of using a sequential code flow (like RTOS or foreground/background) over event-driven code flow (RTEF)?

MiroSpeaker
Score: 1 | 4 years ago | no reply

You might want to watch my last year's presentation, were exactly I explain the advantages and disadvantages of sequential and event-driven paradigm.

KushP
Score: 0 | 4 years ago | no reply
This post has been deleted by the author
MiroSpeaker
Score: 3 | 4 years ago | no reply

I hope you enjoy the presentation. Please join me for live Q&A session. I'm really looking forward to interesting questions and discussion!

15:41:54	 From  Tim Michals : Is there a Linux version of all the tools and examples?
15:42:11	 From  Dave Nadler : Thanks Miro for a great talk. Youu mentioned puub/sub, but the example shows sending events to specific objects? Can you explain?
15:42:12	 From  patelk5 : Is there any advantage of using a sequential code flow (like RTOS or foreground/background) over event-driven code flow (RTEF)? It seems like event-driven is superior in almost every scenario?
15:42:13	 From  Alex Burka : Why not use C++?
15:42:20	 From  patelk5 : Thanks Miro!
15:42:35	 From  afwaanquadri : How do you suggest an AO to handle a blocking call? such as a HTTP request.
15:42:41	 From  Rocco Brandi : I have read Miro's book several years ago. the info were amazing, but are still valid today? are they a little out of date?
15:42:49	 From  Radu Pralea : This approach (Active Objects, State Machines) seems a very elegant way of expressing the problem in a more optimal and robust way compared to a threading approach (eliminating sharing and (some) timing problems and overheads).
However,:
- it seems (at least at a first glance) a less natural way of thinking about the problem domain (more indirect, i.e. designing the sequence diagram with its constraints (no sharing), compared to a closer to the problem domain modeling approach)
- it seems that the sequence diagram doesn't scale very well (I expect it to become less manageable with increasing size/number of concepts, compared to the task oriented design)
- it seems that it's a less agile way of partitioning the problem: it would work fine for a reasonably sized problem that's very well defined beforehand, but it would require very inconvenient redesigns as the requirements keep coming and/or changing
Could you please address (some of) these concerns, please?
15:43:06	 From  jvillasante : How expensive a new requirement (like we want the ship to be able to fire several missiles) would be to add to the current design?
15:43:10	 From  Roman Hvozd : Hi Miro.
Can QSPY traces be turned off, but ability to send (post/publish) commands (signal) to the aplication and trace used data still be available?
15:43:13	 From  Burak ŞEKER : Why not use C++? (I have same question with Alex)
15:43:40	 From  Charles Miller : Hello Miro.  Your first ed. of the book was in C++; the UML version went to C.  Was there pressure to move to the lesser language? (IMHO :-) )
15:44:24	 From  Davy Baker : is it feasible to interact with other visual code generation environments like say labview ?
15:51:35	 From  patelk5 : Are there any concurrency issues to be cognizant of even if Active Objects are used?
15:52:00	 From  Davy Baker : What is the minimum size of flash and ram you have seen work worth Active Objects ?
15:52:30	 From  afwaanquadri : How do you suggest an AO to handle a blocking call? such as a HTTP request.
15:53:24	 From  Gopinath : Great presentation, Dr. Samek. Can Active Objects and Hierarchical State Machines be implemented on bare metal systems? Or do they require RTOS?
15:54:30	 From  patelk5 : @Gopinath: I think the answer is yes. https://www.state-machine.com/qpc/ports_native.html
15:54:46	 From  patelk5 : I did it last weekend on a TI board :)
15:54:53	 From  Burak ŞEKER : To Gopinath: It is possible to use Active Object and Pub/Sub approach in bare metal just fine.
15:55:06	 From  Sam : Are there good SDD's out there that are available for download?
15:56:16	 From  Dave Nadler : Socially Distant Disease?
15:56:29	 From  Sam : software design document
15:57:28	 From  Keith J : What about GUI code generation tools like QT?
15:57:34	 From  Steve Wheeler : Do the QP frameworks generate timing/event diagrams such as the one you showed?
15:57:36	 From  Keith J : interactions
15:57:47	 From  javi : How are events sent and received? using queues.. mailboxes..
15:59:21	 From  Bob Dowling : To Patelk5: which TI board?
15:59:35	 From  patelk5 : TM4C123GXL
16:00:02	 From  Bob Dowling : Thanks! (We use C2000)
16:00:06	 From  patelk5 : If you download qpc, there are bunch of examples in the examples folder for many different boards
16:00:17	 From  Tim Michals : Some of the Q&A sessions need more time ;)
16:00:31	 From  patelk5 : I couldn't agree more :(
16:00:39	 From  Yuriy Kozhynov : Thanks!
16:00:40	 From  David : Thanks Miro!
16:00:41	 From  NateWelch : Thank you Miro
16:00:45	 From  patelk5 : Thank you!
16:00:45	 From  Christopher Long : Thank you Miro for the excellent presentation and Q&A! I am big fan of your YouTube content.
16:00:45	 From  Burak ŞEKER : Thanks a lot
16:00:51	 From  Raul Pando : Great work Miro
16:00:58	 From  javi : thanks!
16:01:03	 From  Gopinath : Thanks to Dr. Samek.

OUR SPONSORS

OUR PARTNERS