This is an old revision of the document!
Generate production ready source code from UML state diagrams
What is Sinelabore?
Sinelabore enables developers to effectively combine event-driven architecture, hierarchical state machines, model-based design and automatic code generation. A payback is usually given already immediately.
What can Sinelabore do for me as a software developer for the IoT or for critical applications?
Many systems are likely candidates for implementation as finite state machines. A system that must sequence a series of actions or that must handle inputs differently depending on the mode it is in is often best implemented as a finite state machine. Typical examples are control-logic-oriented applications such as metering, monitoring, workflows and control applications. For IoT applications where parts of the application are implemented in Java / Python / C# / Lua / Rust / JavaScript / Go or Swift, the code can also be generated in these languages.
What can it do for me as an embedded software developer?
SinelaboreRT focus is on generation of readable and maintainable code from flat or hierarchical UML state machine diagrams. With its unique features the tool covers perfectly the requirements of embedded real-time and low-power application developers coding in C / CPP. The generated code is independent of CPU and operating system.
How do I use it?
Use your existing favourite modelling tool and generate code from it with an easy-to-use command line tool. Or use the built editor to create state machines within minutes. Automatic model checks warn from design flaws. Configure the generation process according to your needs. Simulate your model. Generate trace code automatically if needed. All major state diagram features like hierarchical states, regions, history, sub-machines … are supported.
Does the Code Generator run on my OS?
The Sinelabore code generator runs on any OS that supports a modern Java Version e.g. Windows, Linux, macOS or from within a container.
Can I use the generated code on my embedded platform?
By generating code that can be compiled with virtually any compiler, and the ability to integrate with your existing IDE, build process or continuous integration system, the code generator can be quickly integrated into any project. Configuration is stored in a plain text file which allows customisation of generated code to exactly your needs.
- Generated code has production-quality. It is based on nested
switch/case
andif/then/else
statements. It is easy to read, understand and debug if needed. The generated code requires no compiler specific tricks except standard language features. This means that if the worst comes to the worst, you can easily change or expand the code by hand. - Can be used with any 8-, 16- or 32-bit CPUs. There is no run-time environment needed like with some other solutions.
- Fits well in different system designs. The code generator does not dictate how you design your system. Therefore it is no problem to use the generated code in the context of a real-time operating system (VxWorks, FreeRTOS, embOS, RTEMS, …) or within an interrupt service routine or in a foreground / background (super loop) system.
- There will be no problems when using static code analyzers.
- Generated cpp code passes clang-tidy and is cpp11 ready (modernize-*). Set configuration parameters accordingly.
How Sinelabore Improves Developer Productivity
Avoid bugs that can waste countless hours of developer and end-user time before they are found. Developers spend a lot of their time coding state machines by hand. And have to do it whenever the design changes. Sinelabore avoids the error-prone and tedious hand-coding by generating high-quality source code directly from the state machine design document.
- No gap between design and code anymore. The documentation is always up to date.
- Use the UML tool of your choice: Cadifra UML Editor, UModel, Magic Draw, Enterprise Architect, Astah* / astah SysML, Visual Paradigm, Modelio
- An integrated state diagram editor makes it easy to get started and allows you to create state diagrams within minutes. The entry barrier is significantly lower compared to full-fledged UML tools. A series of tutorials (see sidebar) explains step by step how to use the integrated diagram tool.
- Use the code generator only for those parts of your software that benefit from state machine modeling and code generation. Use your existing development environment for all the other code. The code-generator does not dictate an “all or nothing” approach as many other commercial tools.
- Automatic robustness tests, test-case generation, tracing and simulation
- Extensive Manual with getting started section
Secure, On-site Code Generation
The code generator runs locally on your developer workstations, build servers or continuous integration servers. It does not use an internet connection and will never collect nor submit data, code, statistics, analytics, or any other information from your system over any channel.
Generate code in the shortest time
To get an impression of the powerful capabilities of the tool download the demo version. Checkout the examples folder to see the generated code. Follow the “Getting Started” pages on this website. The manual contains a basic introduction into state-machines in case you need a refresh. Read the sections related to your UML tool and the language backend you want to use. If no UML tool is already in place take a look at the built in state machine diagram editor.
To run the code you have two options.
- Run the examples on your PC. The example folder contains examples for all supported modelling tools and various languages (C, CPP, …). The examples realizes a microwave oven and can be executed and tested. Play with the model and enhance it. Regenerate the code and learn from the warning and error messages.
- Run examples on a Micro-Controller e.g. a MSP430 evaluation board using Energia. An example with all details is available on github.
Customers and what customers say
The Sinelabore Code Generator is used world wide by very well known tier 1 companies and OEMs. But also small engineering companies as well as single developers.
“Sinelabore has helped me implement the behavior of a complex, asynchronous system. All the UML 2 elements I needed are available. I like that I don’t have to draw the state machine, then separately implement it and keep these two synchronized; this saves me time and reduces the potential of bugs. The error checking to make sure the state machine is valid is also useful. —- Daniel Bedrenko / Software Developer @ BPS…tec GmbH”
“Thank you again for providing such great tool!”
“We like Your Tool, infact we will give intro for another local company next week.”
Study done by “Laboratory of Model Driven Engineering for Embedded Systems @ CEA in France” with the title “Complete Code Generation from UML State Machine” write in their report “ … without optimization, Sinelabore generates the smallest executable size ,,,”.
Using State-Machines in (Low-Power) Embedded Systems
Reactive systems are characterized by a continuous interaction with their environment. They typically continuously receive inputs (events) from their environment and − usually within quite a short delay − react on these inputs. Reactive systems can be very well described with the help of state machines. State machines allow to develop an application in an iterative way. States in the state diagram often correspond to states in the application. The resulting model helps to manage the complexity of the application and to discuss it with colleagues from other departments (and domains). Details can be added step by step during the development. Even during creation, the Code Generator can check the state diagrams for consistency (Model Check). Its logic can be simulated and tested. This ensures that the state machine behaves as intended. State machines are very useful for control-oriented applications where attributes such as reliability, code size, power consumption, and real-time behavior are particularly important.
System Architecture
There are different ways how to integrate state machines in a specific system design. Some design principles are more applicable for developers of deeply embedded systems. Others more relevant for developers having not so tight resource constraints.
The SinelaboreRT code generator supports you in the creation of the state based control logic. Generated code fits well in different system designs. The code generator does not dictate how you design your system. Therefore it is no problem to use the generated code in the context of a real-time operating system or within an interrupt service routine or in a foreground / background system.
Using state machines in a main-loop
In this design an endless loop — typically the main function — calls one or more state machines after each other. It is still one of the most common ways of designing small embedded systems. The event information processed from the state machines might come from global or local variables fed from other code or IRQ handlers. The benefits of this design are no need for a runtime framework and only little RAM requirements.
The consequences are:
- All housekeeping code has to be provided by the designer
- Main loop must be fast enough for the overall required response time
- In case of extensions the timing must be carefully rechecked again
Example:
void main(void){ … sm_A(); sm_B(); … }
Using state machines in a main loop with event queue
This design is like the one presented above. But the state machine receives its events from an event queue. The queue is filled from timer events, other state machines (cooperating machines) or interrupt handlers.
Benefits:
- Events are not lost (queuing)
- Event order is preserved
- Decoupling of event processing from event generation.
Consequences:
- A minimal runtime framework is required: Timers and Queues
- Main loop must be fast enough for the overall required response time
A minimal runtime framework for C is available here: https://github.com/sinelabore/examples/tree/master/lib
It offers timers and queues. The intended usage is as follows:
- Each state machine has an own event queue
- Eventually a state machine requires one or more timers (single shot or cyclically).
- A state machine can create as many timers as needed. When creating a timer the event queue of the state machine and the timeout event has to be provided. For different timers it makes sense to provide different timeout events.
- To make the timer work, a tick counter variable has to be incremented cyclically from a timer interrupt (e.g., every 10 ms). The tick frequency should be selected based on the minimal required resolution of the timeout times.
- A tick() function must be called in the main loop to check if any timer has expired. In case a timeout has happened the provided event is stored in the event queue of the state machine.
- The main loop has to check if events are stored for a state machine in its queue. If there are new events they are pulled from the queue and the state machine is called with the event.
Example code with two state machines shows the general principle:
Using state machines in a main loop with event queue, optimized for low power consumption
In low power system designs a key design goal is to keep the processor as long as possible in low power mode and only wake it up if something needs to be processed. The design is very similar to the one described above. The main difference is that the main loop runs not all time but only in case an event has happened. The timer service for the small runtime framework is handled in the timer interrupt.
A skeleton for the MSP430 looks as follows:
The following temperature transmitter using a MSP430F1232 header board with just 256 bytes of RAM and 8K of program memory is based on this design principle. For more information on how to use state-machines in low-power embedded systems see here and here.
Using state machines in interrupts
Sometimes state dependent interrupt handling is required. Then it is useful to embed the state machine directly into the interrupt handler to save every us. Typical usage might be the pre-processing of characters received by a serial interface. Or state dependent filtering of an analog signal before further processing takes place. Using state machines in an interrupt handler can be useful in any system design.
For code generation some considerations are necessary. Usually it is necessary to decorate interrupt handlers with compiler specific keywords or vector information , etc. Furthermore interrupt service handlers have no parameters and no return value. To meet these requirements the Sinelabore code generator offers the parameters StateMachineFunctionPrefixHeader
, StateMachineFunctionPrefixCFile
and HsmFunctionWithInstanceParameters
.
Using state machines with a real-time operating system
In this design each state machine usually runs in the context of an own task. The principle design is shown in the following figure.
Each task executes a state machine (often called active object) in an endless while loop. The tasks wait for new events to be processed from the state machine. In case no event is present the task is set in idle mode from the RTOS. In case one or more new events are available the RTOS wakes up the task. The used RTOS mechanism for event signaling can be different. But often a message queue is used. Events might be stored in the event queue from various sources. E.g. from within another task or from inside an interrupt service routine. This design can be realized with every real-time operating system. Only the event transport mechanisms might differ.
Benefits:
- Efficient and well tested runtime environment provided from the real-time operating system
- Prioritization of tasks, scheduling available
- State machine processing times decoupled from each other.
Consequences:
- Need of a real-time operating system (complexity, ram usage, cost …)
In the how-to section an example of this pattern is presented with FreeRTOS. The examples below shows code for the RTEMS and embOS.
Example code for embOS RTOS from Segger.
Events versus Boolean Conditions
Sinelabore supports two basic modes of operation. Either the generated state machines react on events. Only if an event is present a transition is taken (e.g. evDoorClosed, evButtonPressed). Events are eventually send to the state machine using an event queue (see above). Alternatively transitions are triggered by boolean conditions. If a boolean condition is true a state change happens (e.g. DI0==true). The latter one is useful if binary signals should be processed like shown in these two designs (signal shaping function blocks)(PLCOpen function block). In this case the state machine runs without receiving a dedicated event. Based on the current state, conditions derived from boolean signals are used to trigger state transitions.