saeaadl / code

0 stars 0 forks source link

Improve definition of AADL runtime services and propose a C mapping #1

Open jjhugues opened 3 years ago

jjhugues commented 3 years ago

The code generation annex does not provide a precise definition of RTS. At the time of completion of the previous edition, it has been decided to defer until more implementations are created. We have now reached this state with many tools: Ocarina, RAMSES, ISOCELES and HAMR.

jjhugues commented 3 years ago

Here is an example from A.9 (3)

subprogram Send_Output
features
  OutputPorts: in parameter <implementation-dependent port list>;
  -- List of ports whose output is transferred
  SendException: out event data; -- exception if send fails to complete
end Send_Output;

subprogram Put_Value
features
  Portvariable: requires data access; -- reference to port variable
  DataValue: in parameter; -- value to be stored
  DataSize: in parameter;  - size in bytes (optional)
end Put_Value;

The critical path here is to define a portable way to refer to port variables and to data values.

Ocarina proposes the following approach, implemented as of today

Note that this approach has been extended to the Application RTS: Send_Output, Put_Value, Get_Value, Get_Count, Next_Value, and Updated. Receive_Input has not been made visible at this stage

This approach allows for

Currently implemented

void waterlevelmonitoring_body(__po_hi_task_id self) {

 /* AADL request type, this type is generated form the AADL model,  does not need to be documented in the standard . 
   This provides an abstract DataValue parameter for languages without polymorphism */

  __po_hi_request_t request;  

  /* First we need a container to store the data, this is achieved using the PORT_VARIABLE field of the request. */

  request.PORT_VARIABLE (waterlevelmonitoring_thread, /* thread classifier */
                                             wateralarm /* port name */) = waterlvl;

  /* Then, we send the request through the thread *local* port (i.e. the port on the emitter side). 
    Note: this is a legacy decision, this part can be renamed easily */

  __po_hi_gqueue_store_out /* put_value */
      (self,
       LOCAL_PORT (waterlevelmonitoring_thread, wateralarm),
       &request);
}

Note: since we cannot have function overloading in C, we decided to have one set of functions, and tweak the request type to be polymorphic and be the C union of all relevant thread interfaces. Having one set of function per interface does not respect the initial intent of the standard.

_Another approach like in ARINC653 APEX would have considered a more radical approach based on void */sizet parameter tuple. This would have come at the expense of the analyzability of the underlying middleware, especially the respect of interface types.

Variant#1

A variant might consider self to be a more complex structure that has access to port variables, e.g.

  __po_hi_gqueue_store_out /* put_value */
      (self->wateralarm,
       &request);

However, self->wateralarm is evaluated dynamically, whereas the previous approach can be aggressively optimized by a C compiler thanks to static arguments.

Variant#2

A variant might also consider an additional RTS that provides an allocator for the request, e.g.

void waterlevelmonitoring_body(__po_hi_task_id self) {

 /* AADL request type, allocated (from a pool etc) attached to self */

  __po_hi_request_t *request = new_request (self);  

However, one has to predefine how many instances of request should be pre allocated per task, adding more configuration space. With the solutions above, the request lifecycle is managed by the user.

jjhugues commented 3 years ago

@Etienne13 can you please provide some details on how this is addressed in Ramses?

Etienne13 commented 3 years ago

First of, in RAMSES the signatures of runtime services are gathered in a header file named aadl_runtime_services.h. This is an important starting point as it tells C developers what is the header to include in order to use these services.

The services that are implemented are:

  1. Services accessible to developers:

error_code_t Put_Value(port_reference_t * port, runtime_addr_t value);

error_code_t Send_Output(port_reference_t * port);

error_code_t Next_Value(port_reference_t * port);

error_code_t Get_Value(port_reference_t * port, runtime_addr_t dst);

error_code_t Updated(port_reference_t * port, uint8_t * fresh_flag);

error_code_t Get_Count(port_reference_t * port, uint16_t * count_res);
  1. Internal services

error_code_t Receive_Input(port_reference_t * port);

error_code_t Receive_Input_Thread(thread_config_t * config);

error_code_t Send_Output_Thread(thread_config_t * config);

error_code_t Await_Mode(thread_config_t * config);

error_code_t Await_Dispatch(thread_config_t * config);

error_code_t Error_Handler(thread_config_t * config, error_code_t error)

void Raise_Error (thread_config_t * config, error_code_t error)

You see that it returns an error_cote_t, which is currently defined as:


typedef enum error_code_t
{
  RUNTIME_OK,
  RUNTIME_EMPTY_QUEUE,
  RUNTIME_FULL_QUEUE,
  RUNTIME_LOCK_ERROR,
  RUNTIME_OUT_OF_BOUND,
  RUNTIME_INVALID_PARAMETER,
  RUNTIME_INVALID_SERVICE_CALL,
  RUNTIME_OUTDATED_INPUT, // not fresh for an input port of an immediate connection
  RUNTIME_SYSCALL_ERROR,
  RUNTIME_START_THREAD_ERROR,
  RUNTIME_WAIT_DISPATCH_ERROR,
  RUNTIME_DEADLINE_MISS
} error_code_t;

Note: these services are also described in an aadl_runtime.aadl file in RAMSES. In this model, types and services are "opaque" (only the name of types and associated files are described).

When used in C user code, with the code generation convention named AADL, it looks like this:


#include "aadl_runtime_services.h"
#include "gtypes.h"

void send(__sender_spg_context* ctx)
{
  uint8_t msg;
  Put_Value(ctx->spg_event_port, &msg);
  ...
}

Explanation: __sender_spg_context is a type generated in gtypes.h from a subprogram called sender_spg in the aadl model; __sender_spg_context is a struct containing a field spg_event_port which is of type port_reference_t. It has been generated because as an image of the aadl subprogram port called spg_event_port. It should also contain a field called "config" which points to the thread_config_t in which the subprogram is called.

  1. About recovery entrypoints, they are supposed to have the following signature:
error_code_t the_name_you_want(runtime_addr_t context);

it is supposed to return RUNTIME_OK if it recovered the error; a different error code otherwise. The type of the context is opaque, and can be casted to the generated type for a thread context (__t1_context_t for a thread subcomponent t1 having the_name_you_want as a recovery entrypoint source text).

Note: Error_Handler is called when an error occur in a runtime service. If defined, it calls the recovery entrypoint. If error is recovered, it return RUNTIME_OK. Otherwise, if defined, it sends the error code through the error port of the thread.

jjhugues commented 3 years ago

Some considerations after the posts above:

Changes to the core document

Changes to the code annex

General

C mapping

Unordered