6. PolyORB-HI/C

6.1. About

PolyORB-HI/C is a middleware for High-Integrity Systems, it inherits most concepts of the schizophrenic middleware PolyORB while being based on a complete new source code base, compatible with the Ravenscar profile and the restrictions for High-Integrity systems.

PolyORB-HI/C acts as an execution runtime for the AADL language. In this context, Ocarina acts as a compiler, turning an AADL model into C code that uses low-level constructs provided by PolyORB-HI/C.

The generated code is in charge of allocating all required ressources (threads, buffers, message queue), configure communication stacks, marshallers and concurrency structures.

6.2. Supported Platforms

PolyORB-HI-C has been compiled and sucessfully tested on

Native platforms

  • Linux
  • Mac OS X
  • FreeBSD
  • Windows

Embedded platforms

  • AIR Hypervisor
  • FreeRTOS (alpha stage)
  • RTEMS
  • Xenomai
  • XtratuM

Note

when using RTEMS operating system, you have to define the RTEMS_MAKEFILE_PATH environment variable. See RTEMS documentation for more details.

6.3. Tree structure

PolyORB-HI-C source directory has the following tree structure:

  • doc/: documentation,
  • examples/: set of examples to test PolyORB-HI-C
  • share/: common files (aadl files used by Ocarina, makefiles, …)
  • src/: core of PolyORB-HI
  • src/drivers: device drivers supported by PolyORB-HI-C
  • tools/: some script to handle the packaging and a verification tool to check if the binaries are compliant with the POSIX restrictions
  • COPYING3 and COPYING.RUNTIME: licence information
  • README: short description of the distribution

When installed with Ocarina, in $OCARINA_PATH directory:

  • documentation is in $OCARINA_PATH/share/doc/ocarina
  • examples are in $OCARINA_PATH/examples/ocarina/polyorb-hi-c/
  • runtime files are in $OCARINA_PATH/include/ocarina/runtime/polyorb-hi-c/

6.4. Generating code from an AADL model

To build your own system, you have to select the PolyORB-HI/C backend, either using a scenario file or the command line.

  • To use a scenario file, refer to Scenario files
  • To use command line, you have to select the polyorb_hi_c backend, e.g. by using the command ocarina -g polyorb_hi_c <list-of-aadl-files>. Refer to Ocarina command-line for more details .

6.5. Code generation towards PolyORB-HI/C

6.5.1. The ping example

In the following, and for each component of the distributed application, we give the AADL entities that are used to model this component, and then the transformation rules used by the code generator to generate C code from these entities.

The mapping rules will be illustrated using the following simple example of a distributed application:

@image{fig/ping, 12cm}

The figure above shows the architecture of the ping example: a client, which is a process containing one single periodic thread, sends a message to the server which is a process containing one sporadic thread that handles incoming ping messages from the client. Each node of the ping application runs on a different machine.

In this chapter, we first present the AADL modeling patterns used to define a distributed application. Then, we give the rules applied to map AADL entities onto instances of PolyORB-HI/C elements.

In the following, we detail only the rules that are directly related to the distributed application as a whole system. The rules that are specific to the components of the distributed application are explained in the sections that deals with these respective components.

6.5.2. Mapping AADL system

A full system is captured using a root system. The system implementation shown on the following example models such system.

system PING
end PING;

system implementation PING.Impl
subcomponents
  --  Nodes
  Node_A : process A.Impl;
  Node_B : process B.Impl {ARAO::port_number => 12002;};

  --  Processors
  --  ...
connections
  --  ...
properties
  --  ...
end PING.Impl;

For each node (process) of the system, we instantiate a subcomponent in the system implementation, and bind it to a processor component. This processor will configure the build target.

We use the properties section of the AADL system (see @ref{Hosts` for more details) to map the different nodes on the different platforms of the distributed application. The connections section of the system implementation models the connections between the different nodes of the application.

An AADL system is mapped into a hierarchy of directories:

  • the root directory of the distributed application has the same name as the root system implementation, in lower case, all dot being converted into underscores. This directory is the root of the directory hierarchy of the generated C code.
  • for each node of the distributed application, a child directory having the same name as the corresponding process subcomponent (in lower case) is created inside the root directory. This child directory will contain all the code generated for the particular node it was created for (see @ref{Distributed application nodes} for more details).

6.5.3. Mapping AADL process

We use the process component category to model an application node (e.g. an element that would ultimately become a Unix process, an embedded application or an ARINC653 partition). The process implementation shown in the listing below shows such system.

process A
features
  Out_Port : out event data port Simple_Type;
end A;

process implementation A.Impl
subcomponents
  Pinger : thread P.Impl;
connections
  C1 : event data port Pinger.Data_Source -> Out_Port;
end A.Impl;

For each thread that belongs to a node of the distributed application, we instantiate a subcomponent in the process implementation. For each connection between a node and another, a port feature has to be added to both nodes with the direction out for the source and in for the destination (see @ref{Connections} for more details on connections mapping).

Elements associated to this process (threads, subprograms, data types, etc) are generated in a child directory of the root system directory. This directory has the same name as the process subcomponent instance relative to the handled node in the system implementation that model the distributed application, in lower case.

For example, all the entities relative to the process A.Impl of the Ping example are generated in the directory ping_impl/node_a.

The following paragraphs list the C compilation units that are created for each node of the distributed application.

6.5.3.1. Marshallers functions

The marshallers functions are used to put all request and types values in a message in order to send them through a network connections. All marshalling functions are declared in the file marshallers.c.

However, PolyORB-HI-C can also use third-party marshallers. It can rely on the marshallers generated for ASN1 encoding. Details about ASN1 marshallers are provided in the next section.

@subsubsection Using ASN1 marshallers

With the ASN1 tools from Semantix (see. @url{http://www.semantix.gr/assert/}), you can convert ASN1 declarations into AADL models. Then, these models can be used with AADL components and PolyORB-HI-C relies on Semantix tools to automatically generates C code that implements the ASN1 types.

For that purpose, you need to install the program asn2aadlPlus and asn1cc. These programs are freely available on @url{http://www.semantix.gr/assert/}. Then, when you use ASN1 types with your AADL model (with the AADL files generated with asn2aadlPlus), PolyORB-HI-C uses the generated code from ASN1 descriptions and integrate it to marshall data.

6.5.3.2. Node activity

We denote activity the set of the actions performed by one particular node which are not triggered by other nodes. All the periodic threads of a node are part of the node activity.

The code related to the node activity is generated in an C file with the name activity.c. An example is shown below :

#include <po_hi_types.h>
#include <po_hi_gqueue.h>
#include <request.h>
#include <deployment.h>
#include <types.h>
#include <subprograms.h>
#include <po_hi_task.h>
#include <po_hi_main.h>
#include <marshallers.h>
extern __po_hi_entity_t __po_hi_port_global_to_entity[__PO_HI_NB_PORTS];
extern __po_hi_port_t __po_hi_port_global_to_local[__PO_HI_NB_PORTS];
__po_hi_int8_t __po_hi_data_source_local_destinations[1] = {ping_me_global_data_sink};
__po_hi_uint8_t __po_hi_pinger_woffsets[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_offsets[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_used_size[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_empties[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_first[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_recent[__po_hi_pinger_nb_ports * sizeof(__po_hi_request_t)];
__po_hi_uint8_t __po_hi_pinger_queue[0 * sizeof(__po_hi_request_t)];
__po_hi_uint16_t __po_hi_pinger_total_fifo_size = 0;
__po_hi_port_t __po_hi_pinger_history[0];
__po_hi_uint8_t __po_hi_pinger_n_dest[__po_hi_pinger_nb_ports] = {1};
__po_hi_int8_t __po_hi_pinger_fifo_size[__po_hi_pinger_nb_ports] = {__PO_HI_GQUEUE_FIFO_OUT};
__po_hi_uint8_t* __po_hi_pinger_destinations[__po_hi_pinger_nb_ports] = {__po_hi_data_source_local_destinations};
/*  Periodic task : Pinger*/

/****************/
/* pinger_job   */ 
/****************/

void* pinger_job ()
{
   simple_type data_source_request_var;
   __po_hi_request_t data_source_request;

   __po_hi_gqueue_init(node_a_pinger_k,__po_hi_pinger_nb_ports,__po_hi_pinger_queue,__po_hi_pinger_fifo_size,__po_hi_pinger_first,__po_hi_pinger_offsets,__po_hi_pinger_woffsets,__po_hi_pinger_n_dest,__po_hi_pinger_destinations,__po_hi_pinger_used_size,__po_hi_pinger_history,__po_hi_pinger_recent,__po_hi_pinger_empties,__po_hi_pinger_total_fifo_size);
   __po_hi_wait_initialization();
   while (1)
   {
      /*  Call implementation*/
      do_ping_spg(&(data_source_request_var));
      /*  Set the OUT port values*/
      data_source_request.vars.pinger_global_data_source.pinger_global_data_source = data_source_request_var;
      data_source_request.port = data_source_request_var;
      __po_hi_gqueue_store_out(node_a_pinger_k,pinger_local_data_source,&(data_source_request));
      /*  Send the OUT ports*/
      __po_hi_gqueue_send_output(node_a_pinger_k,pinger_global_data_source);
      __po_hi_wait_for_next_period(node_a_pinger_k);
   }
}


/**************************/
/* __po_hi_main_deliver   */ 
/**************************/

void __po_hi_main_deliver 
      (__po_hi_msg_t* message)
{
   __po_hi_request_t request;
   __po_hi_entity_t entity;

   __po_hi_unmarshall_request(&(request),message);
   entity = __po_hi_port_global_to_entity[request.port];
   switch (entity)
   {
      default:
      {
         break;
      }
   }
}


All the naming rules explained in @ref{Whole distributed application} are also applied to map the package name. This file contains all the routines mapped from the periodic threads that belong to the handled node (see @ref{Threads} for more details on thread mapping). This package contains also the instances of shared objects used in this node (see @ref{Data} for more details). If the node does not contain any periodic thread nor shared objects, there is no activity.c file generated for this node. Thus, the node B in the Ping example does not have a activity.c package.

6.5.3.3. Data types

All the data types mapped from AADL data components and used by a particular node of a distributed application are gathered in a separate C file called types.h.

For more detail on the mapping of data components, see @ref{Data}.

6.5.3.4. Subprograms

The mapping of all AADL subprogram components used by a particular node is generated in a separate file called subprograms.c. The content of the file is shown in the following example:

For more detail on the mapping of subprogram components, see @ref{Subprograms}.

6.5.3.5. Deployment information

The deployment information is the information each node has on the other nodes in the distributed applications. This information is used, to send a request to another node or to receive a request from another node. The deployment information is generated for each node in two C files : deployment.h and deployment.c.

The file deployment.h contains the following types

  • a first type called __po_hi_node_t. For each node in the application we create an enum whose name is mapped from the node instance declared in the system implementation to which we concatenate the string _k. All the naming rules listed in @ref{Whole distributed application} have to be respected.
  • a second type called __po_hi_entity_t. For each thread in the the application, we declare an enum.
  • a third type called __po_hi_task_id. For each thread that run on the current node.
  • a fourth type called __po_hi_entity_server_t. For each node that may communicate with the current node, we add a value in this enum. It will be used by the transport layer. Please note that at least one server is declared : the value invalid_server.
  • a fifth type called __po_hi_port_t that contains all global port identifier.

More, this file contains the following maccros :

  • __PO_HI_NB_ENTITIES is the number of entities in the whole distributed system.
  • __PO_HI_NB_TASKS is the number of the tasks that will be started on the current node
  • __PO_HI_NB_NODES is the number of nodes in the distributed system.
  • __PO_HI_PROTECTED is the number of protected objects use on the current node.
  • __PO_HI_NB_PORTS that represent the total number of ports in the whole distributed system.
  • __PO_HI_NB_DEVICES that represent the total number of devices in the whole distributed system.

The file deployment.c contains the following variables :

  • mynode variable which has the value of the handled node.
  • __po_hi_entity_table variable is used to know on which node an entity runs.
  • __po_hi_port_global_to_local variable is used to convert a global port identifier to a local port identifier
  • __po_hi_port_global_to_entity variable is used to know on which entity a given port is. This table is used convert a global port identifier to an entity identifier.
  • __po_hi_uint8_t __po_hi_deployment_endiannesses variable details which the endianess of each node. It is an array which size is __PO_HI_NB_NODES.
  • __po_hi_port_to_device is an array which size is __PO_HI_NB_PORTS. For each port, it indicates the value of the device identifier that handles it.
  • __po_hi_port_global_model_names is an array which size is __PO_HI_NB_PORTS. For each port, it contains the name of the port.
  • __po_hi_port_global_names is an array which size is __PO_HI_NB_PORTS. For each port, it contains the name generated by the code generator.
  • __po_hi_devices_naming is an array which size is __PO_HI_NB_DEVICES. For each deivce, it contains all relevant information for their configuration. The configuration string is deduced from the Configuration property associated with the device.

The following example shows the Deployment package relative to the node A of the Ping example:

#ifndef __DEPLOYMENT_H_
#define __DEPLOYMENT_H_ 
#include <po_hi_protected.h>
typedef enum
{
   pinger_local_data_source = 0
} __po_hi_pinger_t;

#define __po_hi_pinger_nb_ports 1

typedef enum
{
   ping_me_local_data_sink = 0
} __po_hi_ping_me_t;

#define __po_hi_ping_me_nb_ports 1

/*  For each node in the distributed application add an enumerator*/

typedef enum
{
   node_a_k = 0,
   node_b_k = 1
} __po_hi_node_t;

/*  For each thread in the distributed application nodes, add an enumerator*/

typedef enum
{
   node_a_pinger_k_entity = 0,
   node_b_ping_me_k_entity = 1
} __po_hi_entity_t;

typedef enum
{
   node_a_pinger_k = 0
} __po_hi_task_id;

#define __PO_HI_NB_TASKS 1

/*  For each thread in the distributed application nodes THAT MAY COMMUNICATE*/
/*   with the current node, add an enumerator*/

typedef enum
{
   invalid_server = -1
} __po_hi_entity_server_t;

#define __PO_HI_NB_SERVERS 0

#define __PO_HI_NB_PROTECTED 0

#define __PO_HI_NB_NODES 2

#define __PO_HI_NB_ENTITIES 2

#define __PO_HI_NB_PORTS 2

typedef enum
{
   pinger_global_data_source = 0,
   ping_me_global_data_sink = 1
} __po_hi_port_t;

#endif

6.5.3.6. OS Configuration

A host is the set formed by a processor and an operating system (or real-time kernel).

To model both the processor and the OS, we use the processor AADL component. The characteristics of the processor are defined using AADL properties. For example, if our distributed application uses an IP based network to make its node communicate, then each host must have an IP address. Each host must also precise its platform (native, LEON…). The listing following example shows how to express this using a custom property set.

processor the_processor
properties
  ARAO::location           => "127.0.0.1";
  ARAO::Execution_Platform => Native;
end the_processor;

To map an application node (processor) to a particular host, we use the Actual_Processor_Binding property. The following example shows how the node Node_A is mapped to the processor Proc_A in the Ping example.

system PING
end PING;

system implementation PING.Impl
subcomponents
  --  Nodes
  Node_A : process A.Impl;
  Node_B : process B.Impl {ARAO::port_number => 12002;};

  --  Processors
  CPU_A  : processor the_processor;
  CPU_B  : processor the_processor;
connections
  --  ...
properties
  --  Processor bindings
  actual_processor_binding => reference CPU_A applies to Node_A;
  actual_processor_binding => reference CPU_B applies to Node_B;
end PING.Impl;

The C generated code concerning the code generation to model host mapping is located in the naming.c file. More precisely, the node_addr and node_port contains, for each node, the information related to its host. These information are dependant on the transport mechanism used in the distributed application.

6.5.4. Mapping AADL threads

The threads are the active part of the distributed application. A node must contain at least one thread and may contain more than one thread. In this section, we give the AADL entities used to model threads. Then, we give the mapping rule to generate C code corresponding to the periodic and aperiodic threads.

The rules are listed relatively to the packages generated for the nodes and for the distributed application (see @ref{Distributed application nodes} and @ref{Whole distributed application}). Only rules that are related directly to a thread as a whole subsystem are listed here.

6.5.4.1. AADL entities

AADL thread components are used to model execution flows.

The features subclause of the thread component declaration describes the thread interface. The ports that may be connected to the ports of other threads, enclosing process, etc.

The properties subclause of the thread implementation lists the properties of the thread such as its priority, its nature (periodic, sporadic) and many other properties ares expressed using AADL properties.

The calls subclause of the thread implementation contains the sequences of subprograms the thread may call during its job (see @ref{Subprograms} for more details on the subprogram mapping). If the thread job consist of calling more than one subprogram, it is mandatory to encapsulate these calls inside a single subprogram which will consist the thread job.

The connections section of a thread implementation connects the parameters of the subprograms called by the thread to the ports of the threads or to the parameters of other called subprograms in the same thread.

thread P
features
  Data_Source : event out data port Simple_Type;
end P;

thread implementation P.Impl
calls {
  --  ...
};
connections
  --  ...
properties
  Dispatch_Protocol => Periodic;
  Period            => 1000 Ms;
end P.Impl;

The listing above shows the thread P which belongs to the process A.impl in the Ping example. We can see that P is a periodic thread with a period of 1000ms, that this thread has a unique out event data port and that at each period, the thread performs a call to the Do_Ping_Spg subprogram whose out parameter is connected to the thread port.

6.5.4.2. Mapping rules for periodic threads

Periodic threads are cyclic threads that are triggered by and only by a periodic time event. between two time events the periodic threads do a non blocking job and then they sleep waiting for the next time event.

The majority of the code generated for the periodic threads is put in the activity.c file generated for the application node containing the handled thread. Each periodic thread is created in the main function (main.c file) with the __po_hi_create_periodic_task function-call.

The generated code in the activity.c file is a parameterless function that represents the thread job. The defining identifier of the function is mapped from the thread instance name in the process that models the node, to which we append the string _job. All the naming rules listed in @ref{Whole distributed application} have to be respected. The body of this subprogram calls the subprograms mapped from the subprogram calls the thread performs. Then, it sends the request to the remote threads it may be connected to. Finally, at the end of the function, we make a call to the __po_hi_wait_next_period() with the task identifier as parameter. This call ensure that we wait the next period before we start the function again.

The generated code in main.c file is a function call that creates a periodic task. The task is created with the function __po_hi_create_periodic_task. This creates a periodic task with the wanted properties at the elaboration time of the node. The package instantiation name is mapped from the thread instance name in the process that model the node, to which we append the string _k. All the naming rules listed in @ref{Whole distributed application} have to be respected. The function-call takes the following parameters:

  • the enumerator corresponding to the thread
  • the task period,
  • the task priority. If the user did not specify a priority, then __PO_HI_DEFAULT_PRIORITY is used,
  • the task job which corresponds to the subprogram <Thread_Name>_job.

The following example shows the generated code for the periodic thread Pinger from the node Node_A of the Ping example:

#include <po_hi_types.h>
#include <po_hi_gqueue.h>
#include <request.h>
#include <deployment.h>
#include <types.h>
#include <subprograms.h>
#include <po_hi_task.h>
#include <po_hi_main.h>
#include <marshallers.h>
extern __po_hi_entity_t __po_hi_port_global_to_entity[__PO_HI_NB_PORTS];
extern __po_hi_port_t __po_hi_port_global_to_local[__PO_HI_NB_PORTS];
__po_hi_int8_t __po_hi_data_source_local_destinations[1] = {ping_me_global_data_sink};
__po_hi_uint8_t __po_hi_pinger_woffsets[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_offsets[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_used_size[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_empties[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_first[__po_hi_pinger_nb_ports];
__po_hi_uint8_t __po_hi_pinger_recent[__po_hi_pinger_nb_ports * sizeof(__po_hi_request_t)];
__po_hi_uint8_t __po_hi_pinger_queue[0 * sizeof(__po_hi_request_t)];
__po_hi_uint16_t __po_hi_pinger_total_fifo_size = 0;
__po_hi_port_t __po_hi_pinger_history[0];
__po_hi_uint8_t __po_hi_pinger_n_dest[__po_hi_pinger_nb_ports] = {1};
__po_hi_int8_t __po_hi_pinger_fifo_size[__po_hi_pinger_nb_ports] = {__PO_HI_GQUEUE_FIFO_OUT};
__po_hi_uint8_t* __po_hi_pinger_destinations[__po_hi_pinger_nb_ports] = {__po_hi_data_source_local_destinations};
/*  Periodic task : Pinger*/

/****************/
/* pinger_job   */ 
/****************/

void* pinger_job ()
{
   simple_type data_source_request_var;
   __po_hi_request_t data_source_request;

   __po_hi_gqueue_init(node_a_pinger_k,__po_hi_pinger_nb_ports,__po_hi_pinger_queue,__po_hi_pinger_fifo_size,__po_hi_pinger_first,__po_hi_pinger_offsets,__po_hi_pinger_woffsets,__po_hi_pinger_n_dest,__po_hi_pinger_destinations,__po_hi_pinger_used_size,__po_hi_pinger_history,__po_hi_pinger_recent,__po_hi_pinger_empties,__po_hi_pinger_total_fifo_size);
   __po_hi_wait_initialization();
   while (1)
   {
      /*  Call implementation*/
      do_ping_spg(&(data_source_request_var));
      /*  Set the OUT port values*/
      data_source_request.vars.pinger_global_data_source.pinger_global_data_source = data_source_request_var;
      data_source_request.port = data_source_request_var;
      __po_hi_gqueue_store_out(node_a_pinger_k,pinger_local_data_source,&(data_source_request));
      /*  Send the OUT ports*/
      __po_hi_gqueue_send_output(node_a_pinger_k,pinger_global_data_source);
      __po_hi_wait_for_next_period(node_a_pinger_k);
   }
}


/**************************/
/* __po_hi_main_deliver   */ 
/**************************/

void __po_hi_main_deliver 
      (__po_hi_msg_t* message)
{
   __po_hi_request_t request;
   __po_hi_entity_t entity;

   __po_hi_unmarshall_request(&(request),message);
   entity = __po_hi_port_global_to_entity[request.port];
   switch (entity)
   {
      default:
      {
         break;
      }
   }
}


6.5.4.3. Mapping rules for sporadic threads

Sporadic threads are cyclic threads that are triggered by the arrival of a sporadic event. The minimum inter-arrival time between two sporadic events is called the period of the sporadic thread.

The majority of the code generated for the sporadic threads is put in the activity.c file generated for the application node containing the handled thread. Each periodic thread is created in the main function (main.c file) with the __po_hi_create_sporadic_task function-call.

The generated code in the activity.c file is a parameterless function that represents the thread job. The defining identifier of the function is mapped from the thread instance name in the process that models the node, to which we append the string _job. All the naming rules listed in @ref{Whole distributed application} have to be respected. In the body of the function, the thread will wait for an event (most of the time : a message from another entity).

The generated code in main.c file is a function-call that creates the sporadic task. The task is created with the function __po_hi_create_sporadic_task. This creates a sporadic task with the wanted properties at the elaboration time of the node. The package instantiation name is mapped from the thread instance name in the process that model the node, to which we append the string _k. All the naming rules listed in @ref{Whole distributed application} have to be respected. The function-call takes the following parameters:

  • the enumerator corresponding to the thread
  • the task priority. If the user did not specify a priority, then __PO_HI_DEFAULT_PRIORITY is used,
  • the task job which corresponds to the subprogram <Thread_Name>_job.

The following example shows the generated code for the sporadic thread Ping_Me from the node Node_B of the Ping example.

#include <po_hi_gqueue.h>
#include <po_hi_types.h>
#include <request.h>
#include <deployment.h>
#include <po_hi_task.h>
#include <subprograms.h>
#include <po_hi_main.h>
#include <marshallers.h>
extern __po_hi_entity_t __po_hi_port_global_to_entity[__PO_HI_NB_PORTS];
extern __po_hi_port_t __po_hi_port_global_to_local[__PO_HI_NB_PORTS];
__po_hi_uint8_t __po_hi_ping_me_woffsets[__po_hi_ping_me_nb_ports];
__po_hi_uint8_t __po_hi_ping_me_offsets[__po_hi_ping_me_nb_ports];
__po_hi_uint8_t __po_hi_ping_me_used_size[__po_hi_ping_me_nb_ports];
__po_hi_uint8_t __po_hi_ping_me_empties[__po_hi_ping_me_nb_ports];
__po_hi_uint8_t __po_hi_ping_me_first[__po_hi_ping_me_nb_ports];
__po_hi_uint8_t __po_hi_ping_me_recent[__po_hi_ping_me_nb_ports * sizeof(__po_hi_request_t)];
__po_hi_uint8_t __po_hi_ping_me_queue[16 * sizeof(__po_hi_request_t)];
__po_hi_uint16_t __po_hi_ping_me_total_fifo_size = 16;
__po_hi_port_t __po_hi_ping_me_history[16];
__po_hi_uint8_t __po_hi_ping_me_n_dest[__po_hi_ping_me_nb_ports] = {0};
__po_hi_int8_t __po_hi_ping_me_fifo_size[__po_hi_ping_me_nb_ports] = {16};
__po_hi_uint8_t* __po_hi_ping_me_destinations[__po_hi_ping_me_nb_ports] = {NULL};

/*********************/
/* ping_me_deliver   */ 
/*********************/

void ping_me_deliver 
      (__po_hi_request_t* request)
{

   switch (request->port)
   {
      case ping_me_global_data_sink:
      {
         __po_hi_gqueue_store_in(node_b_ping_me_k,ping_me_local_data_sink,request);

         break;
      }
      default:
      {
         break;
      }
   }
}

/*  Sporadic task : Ping_Me*/
/*  Get the IN ports values*/

/*****************/
/* ping_me_job   */ 
/*****************/

void* ping_me_job ()
{
   __po_hi_port_t port;
   __po_hi_request_t data_sink_request;

   __po_hi_gqueue_init(node_b_ping_me_k,__po_hi_ping_me_nb_ports,__po_hi_ping_me_queue,__po_hi_ping_me_fifo_size,__po_hi_ping_me_first,__po_hi_ping_me_offsets,__po_hi_ping_me_woffsets,__po_hi_ping_me_n_dest,__po_hi_ping_me_destinations,__po_hi_ping_me_used_size,__po_hi_ping_me_history,__po_hi_ping_me_recent,__po_hi_ping_me_empties,__po_hi_ping_me_total_fifo_size);
   __po_hi_wait_initialization();
   while (1)
   {
      __po_hi_gqueue_wait_for_incoming_event(node_b_ping_me_k,&(port));
      __po_hi_compute_next_period(node_b_ping_me_k);
      if (__po_hi_gqueue_get_count(node_b_ping_me_k,ping_me_local_data_sink))
      {
               __po_hi_gqueue_get_value(node_b_ping_me_k,ping_me_local_data_sink,&(data_sink_request));
         __po_hi_gqueue_next_value(node_b_ping_me_k,ping_me_local_data_sink);

      }
      /*  Call implementation*/
      ping_spg(data_sink_request.vars.ping_me_global_data_sink.ping_me_global_data_sink);
      __po_hi_wait_for_next_period(node_b_ping_me_k);
   }
}


/**************************/
/* __po_hi_main_deliver   */ 
/**************************/

void __po_hi_main_deliver 
      (__po_hi_msg_t* message)
{
   __po_hi_request_t request;
   __po_hi_entity_t entity;

   __po_hi_unmarshall_request(&(request),message);
   entity = __po_hi_port_global_to_entity[request.port];
   switch (entity)
   {
      case node_b_ping_me_k_entity:
      {
         ping_me_deliver(&(request));

         break;
      }
      default:
      {
         break;
      }
   }
}


6.5.5. Deployment information

As said in @ref{Distributed application nodes}, the files deployment.h and deployment.c are generated for each node in the distributed application. For each thread port in the whole distributed application, we declare an enumerator in this type. The defining identifier of the enumerator is mapped from the process subcomponent name and the thread subcomponent name as follows: <Node_Name>_<Thread_Name>_K.

For each that that may communicate, we generate the following elements

  • A variable called __po_hi_<thread_name>_local_to_global (in deployment.c) that is used to convert a local port identifier of the thread to a global one.
  • A type __po_hi_<thread_name>_t that will contain on local port identifier.
  • A macro __po_hi_<thread_name>_nb_ports that will contain the number of ports for the thread.

For these elements, all the naming rules listed in @ref{Whole distributed application} must be respected.

#ifndef __DEPLOYMENT_H_
#define __DEPLOYMENT_H_ 
#include <po_hi_protected.h>
typedef enum
{
   pinger_local_data_source = 0
} __po_hi_pinger_t;

#define __po_hi_pinger_nb_ports 1

typedef enum
{
   ping_me_local_data_sink = 0
} __po_hi_ping_me_t;

#define __po_hi_ping_me_nb_ports 1

/*  For each node in the distributed application add an enumerator*/

typedef enum
{
   node_a_k = 0,
   node_b_k = 1
} __po_hi_node_t;

/*  For each thread in the distributed application nodes, add an enumerator*/

typedef enum
{
   node_a_pinger_k_entity = 0,
   node_b_ping_me_k_entity = 1
} __po_hi_entity_t;

typedef enum
{
   node_a_pinger_k = 0
} __po_hi_task_id;

#define __PO_HI_NB_TASKS 1

/*  For each thread in the distributed application nodes THAT MAY COMMUNICATE*/
/*   with the current node, add an enumerator*/

typedef enum
{
   invalid_server = -1
} __po_hi_entity_server_t;

#define __PO_HI_NB_SERVERS 0

#define __PO_HI_NB_PROTECTED 0

#define __PO_HI_NB_NODES 2

#define __PO_HI_NB_ENTITIES 2

#define __PO_HI_NB_PORTS 2

typedef enum
{
   pinger_global_data_source = 0,
   ping_me_global_data_sink = 1
} __po_hi_port_t;

#endif
#include <deployment.h>
__po_hi_entity_server_t server_entity_table[__PO_HI_NB_ENTITIES] = {invalid_server,invalid_server};
__po_hi_node_t entity_table[__PO_HI_NB_ENTITIES] = {node_a_k,node_b_k};
__po_hi_node_t mynode = node_a_k;

The listing above shows the generated __po_hi_entity_server_t and entity_table for the nodes B from the Ping example.

6.5.6. Mapping of AADL ports

Threads can contain one or several ports. To handle them, we declared several arrays in the activity.c

  • __po_hi_<port_name>_destinations : array for each port of
    the thread which contains all destinations of the port.
  • __po_hi_<thread_name>_woffsets : array (size = number of
    ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_offsets : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_used_size : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_empties : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_first : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_recent : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_queue : array (size = size of the global queue for the thread) used by pohic to handle the global queue.
  • __po_hi_<thread_name>_total_fifo_size : variable that contains the size of the global queue. It is the sum of all port size for the thread.
  • __po_hi_<thread_name>_history : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_n_dest : array (size = number of ports in the thread) used by pohic for the global queue of the thread. It contains the number of destinations for each port of the thread.
  • __po_hi_<thread_name>_fifo_size : array (size = number of ports in the thread) used by pohic for the global queue of the thread.
  • __po_hi_<thread_name>_destinations : array (size = number of ports in the thread) that contains all destinations for each port.

6.5.7. Mapping of AADL Connections

The connections are entities that support communication between the application nodes. In this section, we present the AADL entities used to model connection between nodes.

A connection between two nodes of the system is modeled by:

  • The ports features that exist on each one of the nodes. Ports can be declared inside processes or threads. The direction of the port (in, out or in out) indicates the direction of the information flow.
  • The connections section in the system implementation relative to the distributed application and in the process and thread implementations.
system PING
end PING;

system implementation PING.Impl
subcomponents
  --  Nodes
  Node_A : process A.Impl;
  Node_B : process B.Impl {ARAO::port_number => 12002;};

  --  Processors
  CPU_A  : processor the_processor;
  CPU_B  : processor the_processor;
connections
  --  Port connections
  event data port Node_A.Out_Port -> Node_B.In_Port;
properties
  --  Processor bindings
  actual_processor_binding => reference CPU_A applies to Node_A;
  actual_processor_binding => reference CPU_B applies to Node_B;
end PING.Impl;

The listing above shows the connection between the node A and B in the system implementation.

The nature of the port (event port, data port or event data port) depends on the nature of the connection between the two nodes:

  • if the message sent from one node to another node is only a triggering event and contains no data, we create an event port.
  • if the message sent from one node to another node is a data message but it does not trigger the receiver thread, we create a data port.
  • if the message sent from one node to another node is a data message that triggers the receiver thread, we create an @i{event data} port.

In a distributed system, when we send any data to a node, we need to put them in a stream. We call that the marshall operation. On the other hand, find data in a stream is called the unmarshall operation. In each distributed application, we generate marshallers for each types and request. These functions will marshall/unmarshall data in/from a message.

All marshallers functions are generated in a file called marshallers.c. The marshall (or unmarshall) functions for request are prefixed by the string __po_hi_marshall_request_ (or __po_hi_unmarshall_request_). Marshall (or unmarshall) functions for types are prefixed by the string __po_hi_marshall_type_ (or __po_hi_unmarshall_type_). Each function has the name of the type or the request it marshalls.

Finally, a function __po_hi_marshall_request and __po_hi_unmarshall_request is generated to handle all requests. Then, is called the appropriate function to call to marshall or unmarshall the data.

#include <types.h>
#include <po_hi_types.h>
#include <po_hi_marshallers.h>

/***************************************/
/* __po_hi_marshall_type_simple_type   */ 
/***************************************/

void __po_hi_marshall_type_simple_type 
      (simple_type value,
      __po_hi_msg_t* message,
      __po_hi_uint16_t* offset)
{

   __po_hi_marshall_int(value,message,offset);
}


/*****************************************/
/* __po_hi_unmarshall_type_simple_type   */ 
/*****************************************/

void __po_hi_unmarshall_type_simple_type 
      (simple_type* value,
      __po_hi_msg_t* message,
      __po_hi_uint16_t* offset)
{

   __po_hi_unmarshall_int(value,message,offset);
}


/************************************************/
/* __po_hi_marshall_request_ping_me_data_sink   */ 
/************************************************/

void __po_hi_marshall_request_ping_me_data_sink 
      (__po_hi_request_t* request,
      __po_hi_msg_t* message,
      __po_hi_uint16_t* offset)
{

   __po_hi_marshall_type_simple_type(request->vars.ping_me_global_data_sink.ping_me_global_data_sink,message,offset);
}


/**************************************************/
/* __po_hi_unmarshall_request_ping_me_data_sink   */ 
/**************************************************/

void __po_hi_unmarshall_request_ping_me_data_sink 
      (__po_hi_request_t* request,
      __po_hi_msg_t* message,
      __po_hi_uint16_t* offset)
{

   __po_hi_unmarshall_type_simple_type(&(request->vars.ping_me_global_data_sink.ping_me_global_data_sink),message,offset);
}


/*************************************************/
/* __po_hi_marshall_request_pinger_data_source   */ 
/*************************************************/

void __po_hi_marshall_request_pinger_data_source 
      (__po_hi_request_t* request,
      __po_hi_msg_t* message,
      __po_hi_uint16_t* offset)
{

   __po_hi_marshall_type_simple_type(request->vars.pinger_global_data_source.pinger_global_data_source,message,offset);
}


/***************************************************/
/* __po_hi_unmarshall_request_pinger_data_source   */ 
/***************************************************/

void __po_hi_unmarshall_request_pinger_data_source 
      (__po_hi_request_t* request,
      __po_hi_msg_t* message,
      __po_hi_uint16_t* offset)
{

   __po_hi_unmarshall_type_simple_type(&(request->vars.pinger_global_data_source.pinger_global_data_source),message,offset);
}


/******************************/
/* __po_hi_marshall_request   */ 
/******************************/

void __po_hi_marshall_request 
      (__po_hi_request_t* request,
      __po_hi_msg_t* message)
{
   __po_hi_uint16_t offset;

   offset = 0;
   __po_hi_marshall_port(request->port,message);
   switch (request->port)
   {
      case ping_me_global_data_sink:
      {
         __po_hi_marshall_request_ping_me_data_sink(request,message,&(offset));

         break;
      }
      case pinger_global_data_source:
      {
         __po_hi_marshall_request_pinger_data_source(request,message,&(offset));

         break;
      }
      default:
      {
         break;
      }
   }
}


/********************************/
/* __po_hi_unmarshall_request   */ 
/********************************/

void __po_hi_unmarshall_request 
      (__po_hi_request_t* request,
      __po_hi_msg_t* message)
{
   __po_hi_uint16_t offset;

   offset = 0;
   __po_hi_unmarshall_port(&(request->port),message);
   switch (request->port)
   {
      case ping_me_global_data_sink:
      {
         __po_hi_unmarshall_request_ping_me_data_sink(request,message,&(offset));

         break;
      }
      case pinger_global_data_source:
      {
         __po_hi_unmarshall_request_pinger_data_source(request,message,&(offset));

         break;
      }
      default:
      {
         break;
      }
   }
}


6.5.8. Mapping of AADL Subprograms

Subprograms are used to encapsulate behavioural aspects of the application.

To model a subprogram, we use the subprogram AADL component category. The parameters of the subprogram are specified in the features subclausen of the component declaration. If the subprogram does only the job of calling other declared subprograms, then the calls subclause of the subprogram implementation has to contain such calls. To point to the actual implementation of the subprogram, we use the AADL properties.

The following example shows the AADL model for the Do_Ping_Spg from the Ping example. It precises that the C implementation of the subprogram is located in the function user_ping. The file which contains this function must be stored with the aadl model.

Subprograms are generally called by threads or by other subprograms. To express this, we use the calls subclause of a component implementation. Then we perform all the connections between the called subprograms parameters and the caller components ports (or parameters if the caller is a subprogram).

The following listing shows the calls and connections sections of the periodic thread P in the Ping example.

subprogram Do_Ping_Spg
features
  Data_Source : out parameter Simple_Type;
properties
  source_language => C;
  source_name     => "user_ping";
end Do_Ping_Spg;

Each subprogram instance model a hand-written function. In the subprograms.c file, we declare the definition of this function and we generate a new one that will call the one provided by the user.

The following listing shows the calls and connections sections of the subprogram ping_spg in the Ping example.

#include <types.h>
#include <subprograms.h>
void user_do_ping_spg 
      (simple_type* data_source);
/*****************/
/* do_ping_spg */ 
/*****************/

void do_ping_spg 
      (simple_type* data_source)
{

   user_do_ping_spg(data_source);
}


For each subprogram call in a thread, we generate an C subprogram call to the subprogram implementing the thread and given by mean of the AADL properties.

On the client side, the function sth_Job begins by calling the subprogram in its call sequence. then it calls the stubs of all the subprogram it is connected to.

On the server side, and in the function of the process_request, the subprogram implementation corresponding to the operation (coded in the message) is called.

6.5.9. Mapping of AADL data

The data are the messages exchanged amongst the nodes of the application.

AADL data components are used to model data exchanged in the distributed application. Properties are used to precise the nature of the data. To model a data structure (which contains fields of others data types) we use data component implementation and we add a subcomponent for each field of the structure.

The simple data types that can be modeled using AADL are

  • boolean,
  • integer,
  • fixed point types,
  • characters,
  • wide characters
--  Boolean type

data Boolean_Data
properties
  ARAO::Data_Type  => Boolean;
end Boolean_Data;

--  Integer type

data Integer_Data
properties
  ARAO::Data_Type  => Integer;
end Integer_Data;

--  Fixed point type

data Fixed_Point_Type
properties
  ARAO::Data_Type    => Fixed;

  ARAO::Data_Digits  => 10;
  --  The total number of digits is 10

  ARAO::Data_Scale   => 4;
  --  The precision is 10**(-4)
end Fixed_Point_Type;

--  Character type

data Character_Data
properties
  ARAO::Data_Type  => Character;
end Character_Data;

--  Wide character type

data W_Character_Data
properties
  ARAO::Data_Type  => Wide_Character;
end W_Character_Data;

The complex data types that can be modeled using AADL are

  • Bounded strings
  • Bounded wide strings
  • Bounded arrays of a type that can be modeled
  • Structure where the fields types are types that can be modeled
-- Bounded string type

data String_Data
properties
  ARAO::Data_Type  => String;
  ARAO::Max_Length => <User_Defined_Length>;
end String_Data;

-- Bounded wide string type

data W_String_Data
properties
  ARAO::Data_Type  => Wide_String;
  ARAO::Max_Length => <User_Defined_Length>;
end W_String_Data;

-- Bounded array type: Only the component implementation should be
-- used in the ports or parameters!

data Data_Array
properties
  ARAO::Length => <User_Defined_Length>;
end Data_Array;

data implementation Data_Array.i;
subcomponents
  -- Only one subcomponent
  Element : data String_Data;
end Data_Array.i;

-- Data structure type: Only the component implementation should be
-- used in the ports or parameters!

data Data_Structure
end Data_Structure;

data implementation Data_Structure.i;
subcomponents
  Component_1 : data String_Data;
  Component_2 : data W_String_Data;
  Component_3 : data Data_Array.i;
end Data_Structure.i;

Data components may also contain subprogram features. Depending on the AADL properties given by the user. These component may denote a protected object or a non protected object. In either case, they are used to model a data structure that can be handled only by the subprograms it exports (which are the feature of the data structure).

@include protected_object_types.texi

The example above shows an example of a protected data component (@code{Protected_Object.Impl}). The object has a single field (subcomponent) which is a simple data component. Note that the description of the feature subprograms of these data component is a little bit different from the description of classic subprograms: each feature subprogram must have a full access to the internal structure of the object type. To achieve this, we use the @code{require data access} facility of AADL. To model a non protected data component, user should simply change the @code{ARAO::Object_Kind => Protected;} into @code{ARAO::Object_Kind => Non_Protected;} in the implementation of data component.

@subsection C mapping rules

Data component declaration are mapped into C type declaration in the file @code{types.h}. In the following we give the C type corresponding to each data component type that could be modeled.

@subsubsection Simple types

Simple data components are mapped into an C type definition whose defining identifier is mapped from the component declaration identifier (with respect to the naming rules listed in @ref{Whole distributed application}) and whose parent subtypes is: @itemize @bullet

@item @code{int} for boolean data types @item @code{int} for integer data types @item @code{float} for fixed point types @item @code{chat} for character data types

@end itemize

@subsubsection Bounded strings and wide strings

Bounded strings and wide strings are not supported in the C generator at this time.

@subsubsection Bounded arrays

Bounded arrays and wide strings are not supported in the C generator at this time.

@subsubsection Data structures

Data structures are mapped into a C structure defined in the file @file{types.h}. The identifier of the record type is mapped from the data component name with respect to the naming rules given in @ref{Whole distributed application}. Each field defining identifier is mapped from the subcomponent name given in the data component implementation with the same naming rules. The type of the field is the C type mapped from the data corresponding component. The following example shows the C mapping of the data structure defined given earlier in this part.

@include data_struct.h.texi

@subsubsection Object types

Protected object types are mapped into an a C structure. We add automatically a member in the structure with the type @code{__po_hi_protected_id} and the name @code{protected_id}. This member will identify the protected type in the distributed system. All other members of the object are declared as in Data Structures (see previous subsection). The features subprograms of the object types are declared in the @file{types.h} file, whereas the body of these functions are defined in the @file{types.c} file. Moreover, the value of the @code{protected_id} must be initialized. This is done in the main function (@file{main.c}), before the initialization. All the naming conventions given in @ref{Whole distributed application} have to be respected. The following example shows the specification of the protected type mapped from the @code{Protected_Object.Impl} shown earlier in this part. We show the files @file{types.h}, @file{types.c} and @file{main.c} (that initialize the @code{protected_id} member of the structure.

@include toy_types.h.texi @include toy_types.c.texi @include toy_main.c.texi

Non protected object types are mapped similarly to protected object types. The only difference, is that instead of creating a protected type, we create a generic parameterless nested package.