DioneOS Description

seq_diagram6_blue.png

DioneOS (pronounced /djoneos/) is Real Time preemptive Operating System designated for microcontrollers.
Currently, the system works on ARM Cortex-M3, Texas Instruments MSP430, MSP430X.
This documentation is devoted to the version of the DioneOS working on ARM Cortex-M3. It was tested on ST Microelectronics STM32L162 microcontroller.

Why you need a system for development of your firmware?
What were the main goals?
System testing and reliability.
System features.
System performance.
Limitations and requirements
The price and licensing information (the page contains information how to purchase the system)

Why you need a system for development of your firmware?
If you developed a firmware you probably already faced a problem of sequential execution of a program. Even if the firmware is not very complex, it contains many jobs* that should work concurrently. For example, the firmware may consists of: main state machine controlling behaviour of the device, command processor, modem control module, handler of events on input pins etc. Each of these parts has its own requirements on CPU processing time. Their execution depends on input events that may trigger their activity or set into waiting state. They also need interconnection mechanism for mutual communication. For sure, they share some common data and access it from different locations asynchronously. As long as you don't use operating system you need to solve that problems by yourself.
In most cases it results in unclear structure of the firmware and potential threat of errors that are difficult to be tracked and eliminated. Informal structure cannot be properly described by a model and analysed on higher level of abstraction. Hence, it is difficult to convince oneself that the firmware has no design errors that would cause concurrency issues. In the naive design, interrupts are often disabled to guard access to common objects and many part of the code runs in interrupt service routines (ISR). Programmer must deal with the problem of switching CPU between the jobs - solved by compromised run-to-completion and voluntary redirection of the execution to other job. Another issue is when the job starts waiting for an event, or need to signal to others that something must be done. In that case it needs to trigger an action that must be done in different job. Finally, even if all problems seems to be solved, the structure of the code is affected by parts that imitate concurrency. The jobs are not well separated but form common code, that is difficult maintain and analyse. It is not good practice and design pattern for the firmware where quality requirement is very important.
The alternative is an operating system that provides all universal functionality that is required for semi-parallel execution of the jobs. The system provides a concept of threads - separated parts of the code that can implement only their functions. If the system is preemptive (like DioneOS) it can switch CPU from one thread to another automatically. The system provides typical, well known mechanisms for mutual synchronization (i.e. mutex, semaphore), time dependencies (timers) and communication between the threads (event, queue). When you use the system, all above features are ready when you start development of your firmware. You can focus on your application, because other work is already done and available for use. Moreover, standard elements and design patterns provided by the system force better firmware structure and clarity. It is also much easier to analyse the system behaviour because higher level model can be defined.
What were the main goals?
During the DioneOS system design and development I had always in mind a target and its resources. The main goal was performance. Due to this requirement, many parts of the code has been optimised, some items are build in preprocessing or compilation time. System code responsible for switching between threads (i.e. context switching) has been written in assembler and optimised. Sequence of instructions generated by a compiler were checked and C source code was tuned if it was necessary.
I considered cooperation between threads and ISR and provided fast mechanism for context switching just after asynchronous trigger from ISR (some other systems do context switch only on system tick interrupt, that has disadvantage of increasing latency of triggering a thread from ISR). I tested and optimised switching time for minimal number of cycles spent in this function. Current version of the system (on ARM Cortex-M3,&nbspfclk=32MHz) makes scheduling and context switch in 3.2us and makes complete sequence from signaling in one thread to releasing object and continuation in another in ~8us - 10us depending on switch type and accompanying events (refer to description for more information performance).
Reliability and system testing
Having in mind safety and reliability of the applications that would be developed using DioneOS system, we did carefully testing of the system source code (each line of the source as well as each condition were verified). The system is checked by testing environment that works automatically and verify if all modules work according to design specification. The set of tests consists of more that 400 tests. In the testing procedure also advanced behaviour of Cortex-M3 core and its influence on the system has been checked with positive result (just to mention that we checked all possible types of interrupt preemption cases and considered such properties of the core like 'late arriving interrupt' or 'tail chaining'). If you are interested more in testing the system please read this.


System Features

System type
- multithread (read more about threads)
- preemptive
- maximum priority (each thread has unique priority)
- context switch on:
synchronization object change,
ISR exit,
timer expiration
- preemption control (independent on interrupt control)
Synchronization objects
- mutex (nested calls in one thread are allowed),
- counting semaphore (may be signalled from ISR),
Timing control
- timers (with callback function)
Communication
- ring buffer for non-uniform items (effective implementation of queue)
State machines
- Finite state machines support for coding behavior
- unified events defined for communication
- Active state machine manager that performs the state machine control
Memory management
- memory pool (effective allocator, free of fragmentation issue)
- function for signalling events on chip pins (useful when tracking time dependencies and real time bugs )
- critical exception os_bug() thrown in many system places when unrecoverable bug appears, when system behaviour would be undefined and unpredictable. The exception mechanism is available for user, he can define his own exception codes and use it in application.
- marking deleted objects with specific pattern (useful in detection the usage of non-existing object)
- Many user configurable flags, that affects compiled code of the system
- User can configure and assign processor resources that are used for the operating system (chose the hardware timer, frequency of system tick interrupt, debug port for logic analyser)
Compilers compatibility
ARM Cortex-M3: GNU GCC

If you need more details refer to description in respective files.


Limitations and Requirements

The system uses a few assumptions that helped to achieve better performance of the system functions:
* The system must be compiled under:
* (ARM Cortex-M3) GNU GCC
* Main objects are created at the system start-up (e.g. threads, memory pools, system tick timer), some of them cannot be deleted (e.g. threads).
* Threads have unique priorities
* Maximum number of threads is 16 (including obligatory IDLE thread)
* The idle thread has lowest priority and must always be ready (may be used to determine system load)
* (ARM Cortex-M3) System requires two preemption priority groups, that cannot be shared with other ISRs. Nested interrupts are allowed.
* ISR uses interrupted thread stack for its execution
* Used microcontroller must have enough RAM and FLASH memories for system code, structures and threads requirements (e.g. stack). Below you may find some estimations:
Current version of the system uses following number of bytes for main structures: 106 + Threads*20 ( Cortex-M3: ((Threads+1)*36)), that is 426 bytes for 16 threads. You must also reserve recommended minimum 130 bytes for stack per each thread, hence for 16 threads it would use ~3200 bytes including 160 bytes for initial C stack. This amount does not take into account memory pools and other user variables. Size of thread stack depends on number of nested calls, local variables, etc. Please note that 16 Threads is top bound, if you use less threads in your firmware you can save the RAM.
According to FLASH requirement, the system functions can fit into a few or dose KB depending on a set of items that are used. It is not a problem because most of microcontrollers are equipped with more than 64KB of FLASH.

*) I intentionally used word 'job' to describe some abstract part of activity and avoid any connotation with task or thread.
**) ISR - Interrupt Service Routine