Using modularization in embedded OS

Modern embedded OS are designed to be used in control solutions in various hardware contexts. Control computers may differ in the architecture of the CPU, the structure of communication channels, supported communication protocols, etc. Embedded OS are often statically configured to create an OS image, which intended to be executed on some specific control computer. System integrator usually performs this configuration. Embedded OS are often developed by many companies. Joint development and integration is very complex if OS doesn’t support modularity. Support of modularity and component assembly reduces the need of communication among companies during development and integration. This allows customers to create minimal solutions that are optimally adapted to the particular task and hardware platform. Furthermore, customers may be interested in adding their own low level components without OS modification. In this article, we present an approach to building modular embedded solutions from heterogeneous components based on the RTOS JetOS. The mechanism of components binding developed by us allows uniting heterogeneous components from different manufacturers within the same section of the address space. This mechanism allows component developer to independently develop their components. And system integrator can independently from developers configure what component he likes to see in OS image and how components should interact.


Introduction
Embedded operating systems are built to provide specific functionality on specific hardware.Development of a new OS from scratch for every task and hardware is unwise and operating systems are designed to support several CPU architectures and a lot of peripheral devices in a single distribution.Therefore, OS distribution contains many drivers to support a large number of different hardware.Most of the drivers are not needed for correct OS execution on a specific board.Moreover, many embedded systems are aimed to run in restricted environment, for example with limited memory.Static OS configuration is used in cases when it is known in advance, on which hardware the OS image is going to be executed.OS configuration is commonly performed by the system integrator.They choose OS features suitable for OS task and drivers for hardware.Only chosen parts will get into final OS image.System integrator doesn't change OS source code.Static configuration allows keeping final image small.Safety-critical systems must be certified.For airborne systems there is a standard for certification called DO-178C [1], where OS kernel must be certified by highest level of reliability.Certification is complex and lengthy process.Small change in one part of system leads to recertification of the whole system.
We develop an open-source real-time operating system for civil aircraft airborne computers called JetOS.JetOS is ARINC-653 [2] compliant, supports static configuration and aimed to DO-178 certification.ARINC-653 specifies interfaces that RTOS (real-time operating system) should provide to avionics software, also the standard specifies some design constrains to the OS.The most pertinent constraint is that application code is executed inside partitions that are isolated from each other by resources and in time.
To simplify and minimize OS kernel and therefore to simplify OS certification process we moved drivers and some services from kernel to special ARINC-653 partitions, called system partitions [3].Besides drivers system partition contains services such as network stacks, file systems, logging, etc. System partitions should be certified as well as the kernel.Certification for highlycritical software requires absence of unreachable code.Usage of static configuration of the system partition allows to static selection of required drivers and services, and therefore getting rid of unused code.It is common that there are many vendors involved in building a specific embedded solution: OS vendor, BSP vendor, device driver developers, system integrator, etc.When services or drivers they are developing are strongly coupled, developers have to interact a lot.Therefore splitting system partition to independent isolated components seems to be suitable solution.Each driver and service will be in dedicated component.Each component would have a single developer.Component should interact with each other.Appearance of fixed interface between components would make component development easer.Moreover fixed interface can make system flexible.Statically configured component-based system (in our case system partition) can be flexible in several aspects:  When there are several components implementing the same interface (e.g.several file systems) and system integrator can choose which component will get into final image. When there are several components implementing the same interface, and they all can get into final image.System integrator configure on static, which components interact.For example, if there are two file systems, some component would work with one file system and others with the second one. When system integrator can add new component between two interacting, if the new component has a suitable interface.This is useful and can be used, for example, to insert traffic analyzer between protocol stack and network card driver.Another use-case is to reuse a device driver in an applications stack, such as network card driver in the network stack.Isolated into component the same driver code might serve multiple device instances due to different sets of internal states and configuration parameters.All copies of the component share same driver code, so that each component copy would work with assigned device, would make system scalable and flexible.Certification of system includes, among others, unit and integration tests.Splitting system partition to components makes certification easier.Component-level tests can be run by component developer.And system integrator doesn't need to rerun unit tests, he only needs to run integration tests.е.

Related Works
Classical distributed components models like Enterprise JavaBeans, CORBA, Corba Component Model and DCOM [4,5,6] define components and interfaces between them.Models allow substituting one component with the other one with the same interfaces.Components configuration dynamically configured by brokers.This approach is not suitable for embedded systems with static configuration.Ideas to separate OS appeared long ago in microkernels.Microkernel architecture's [7,8,9] primary goal is to separates OS into independent servers that could be isolated from each other.Servers interact through inter-process communication (IPC).IPC calls are typed and servers with the same interface can substitute one another.But there cannot be two servers with the same interface; therefore this model is not suitable for our tasks too.VxWorks is a popular embedded operating system.VxWorks board support package (BSP) is divided into components.Components interface is declared in component description language (CDL).BSP developer can construct BSP from existing component and can add their own components.But this system is not flexible; for example, each component has hardcoded in it a list of names of components it interact 286 with, therefore one component cannot be easily substituted in a configuration with another one with the same interfaces.We are not aware of any component based model with the following set of features:  Static configuration,  Low overhead,  Flexible configuration (in all aspects from introduction),  Low mishit probability, when component interact with component it not designed to (runtime addressing checks)

Basic Capabilities of Component-Based Model
Our model aimed to have small overhead, so it can be suitable for RTOS.In its raw form, our model assumes that there is a lot of similar code written by component developers in C language.To reduce the amount of hand work we generate helper code, based on configuration files.Language, which is used to write configuration files, can be any declarative language; we use YAML for these purposes.

System integrator view
System integrator decides how many instances of each component should be created, and how they are connected.For each component, they choose unique name, and how to initialize its state.System integrator uses instance names and names of their ports to link ports of different instances.All of this information integrator specifies in configuration file.Graphical view of example configuration use can see at fig. 4.

Active and reactive components
All components with input ports are reactive, i.e. get control by call from other component.Some components are active, i.e. the component gets control from OS by some regularities (periodically or by event).Component can be active and reactive at the same time.There are two types of active components in our model:  Components which have a special entry point -activity.This type of active components is useful when component instances should do some simple work from time to time (for example, checking whether there are any new networks packets).Component developer declares activity name in configuration.All activities are called sequentially.This type of active components has a big disadvantage: if some instance will freeze in its activity then all instances of this type in the system are going to freeze, so component developer should not use any wait objects in activity. Components, which instances create their own threads inside init function.
In this case freezing of the instance, which is running in the dedicated thread, will not cause freezing other instances.

Array of ports
Sometimes component developers need to create configurable number of ports of the same type.We support array of ports, but only for output ports.For calling function of output port array developers should specify index in the array besides port name, function name and function arguments.Arrays of ports are useful in components like router (at the fig.5).Router sends data to configurable of instances.Integrator in the configuration specifies number of elements in port array and their linkage with instances.

Memory blocks
Component instances in our system cannot use system heap, because there can be heap underflow with many instances and not enough heap size.Access to heap and physical (for drivers) memory is done through ARINC-653 memory blocks.For each memory block component developer specifies:  memory block name suffix  memory size  memory alignment  flag, that shows if this memory block used by single instance or shared between instances. physical address for drivers working with memory mapped devices.Memory blocks with fixed physical address must be shared.Name of shared memory blocks is identical to name suffix from configuration.Name of non-shared memory block is concatenation of instance name and memory block name suffix.Instances can access memory blocks by ARINC-653 API specifying memory block name.

Memory ownership
This part of the paper does not describe a feature of our approach.Here is some consideration on memory ownership.Let us consider a component based system partition, which implement networking.There can be a track of components: Message_sender UDP_IP_sender  Eth_sender Network_card_driver. Message sender sends pointer message to UDP_IP_sender; UDP_IP_sender prepends message with UPD and IP header and sends message to Eth_sender; Eth_sender prepends message with Ethernet header and sends to Network_card_driver. Should be specified how own memory and responsible for memory allocations.If UDP_IP_sender and Eth_sender components would allocate buffers in their own memory, then this would greatly complicate their code, as they should also free buffers.Our real time C library does not support memory freeing because memory freeing can make indeterminate amount of time.
To simplify implementation and reduce overhead we used an approach when Message_sender allocates enough memory for all headers (component gets this value from configuration), copies message at the needed offset and pass to next layer pointer to message, message size, prepend and append values.Prepend describes how many bytes before message are allocated.Append describes how many bytes after message are allocated.UDP_IP_sender to add header moves pointer it gets from Message_sender and decreases prepend value to header size.

Future work
We are going to work on supporting component distribution by binary images.This can be used to protect intellectual property of component developer, who does not want to share component source code.Currently system integrator should specify component instances and their linkage in YAML language.We are going to support AADL language, which allows system integrator to graphically create and link instances.To work with AADL we are going to use MASIW framework.MASIW [10,11] (MASIW -Modular Avionics System Integrator Workplace) is s an open source Eclipse-based IDE for development and analysis of AADL models.In addition, we are going to research possibility of using dataflow language to specify component, so that there will be no need to write component implementation in C language

Conclusion
In the paper, we presented a component-based approach that was created for JetOS, but can be used in other systems.The approach turned out to be efficient; it has low overhead and make system flexible and scalable while statically configured.

Fig 1 .
Fig 1. Data_sender interface with one function ret_t send(int) Component type declaration contains component name, component instance state structure, and component ports.Output ports are declared as pair of port name and port interface.Input ports are declared as triple (n, I, m): port name n, port interface I, and m a list of pairs of interface function and assigned implementation specified by components function name.You can see example of component type configuration at fig. 2.

Fig 2 . 3 Fig 3 .
Fig 2. Component type Filter.Component state contains one field edge.Componet type has single input port called in, port interface is data_sender, fucntion send of data_sender interface is implemented by filter_send function.During system build configuration files are parsed and corresponding C code is generated:  C-structure describing component, with name identical to component name.(e.g.structure Filter for component Filter)  Declaration of functions specified in input ports (e.g.declaration of function filter_send for component Filter).This declaration enforces naming convention. Special function for calling output ports.Component developers should use only ports to communicate with other components.Direct call of another component might work but is not guaranteed.The component developer is guaranteed only the interfaces.The developer chooses names for ports.Input ports are an entry point to component.Component developer does not use names on input ports.Output ports are used when component should use service of

Fig 4 .
Fig 4. Example linkage configuration.Sensor_1 and Sensor_2 are instances of Sensor component type.Filter_1 and Filter_2 are instance of Filter component type.Sensor_1 ouput port connect to Filter_1 input port.Filter_1 input port connected to Printer.Same for Sensor_2 and Filter_2 4. Advanced capabilities of component based model 4.1 Init function Instances can have init function: component developers should declare init function name in configuration.At system partition start all init functions of all instances are called sequentially.There is no way to specify dependencies on init (i.e.init of open component should be called before init of the other one) because we assume that components are independent and should not have any dependency.

Fig. 5 .
Fig. 5. Router has an array of out port which are connected to instances handler_1, handler_2 and handler_3 Ключевые слова: встраиваемые системы, модульность, компоненты, ОСРВ DOI: 10.15514/ISPRAS-2017-29(4)-19 Output ports, which are used by a component when invokes behavior of another component.The component calls others indirectly, through output ports.Ports are typed, input port of one component and output of the other one can be connected only if they have the same port type.Port type is called interface.Interface is the set of functions, which input port provides or output port require.Since interface can have several functions, then output port that implements this interface has several assigned handlers, one for each function in interface.Interface declares as the set of triple of function names, signature, and return types.Example of simple interface declaration can be seen at fig.1.
Model defines component types and component instances.Each component has a unique component type and assigned implementation and any number of instances.Component type is similar to term "class" from object oriented languages and component instance is similar to "class objects".Component instances share code, but sharing does not apply to some private data, called instance state.Components interact.The ability of one component to use services of the others is achieved through typed ports.There are two kinds of component ports: Input ports, which show that the component provides some functionality.Input ports have assigned handlers implemented by the component, which will be called when some other component calls the interface of the component.