AUTOSAR-Configurations: Incomplete Configurations May Result in Unstable Systems

First published in Elektronik automotive edition 12/15

Incomplete configurations or misuse of generated APIs may result in unstable systems or sporadically inconsistent data. The following article explains how to avoid this.

Software for Electronic Control Units (ECUs) is becoming increasingly complex. In order to manage this complexity, most new developments use AUTOSAR as their software architecture. Such a system provides a variety of different configuration options, which have to be set completely and correctly by the software architect (or the software integrator).

Essential parts of an AUTOSAR system are Software Components (SWCs) and their Ports. An SWC can consist of one or several executable parts, the so-called Runnable Entities which have to be mapped to Tasks during integration. Runnables of one and the same SWC can be executed in different Tasks as shown for SWC1 in diagram 1.

AUTOSAR configuration 1

In order to enable Runnables to read (or write) data from (or to) Ports, so-called Access Points have to be defined by the software architect during configuration. Finally, the RTE-Generator uses the Access Points to create corresponding APIs for the actual code implementation. The mentioned example with Access Point dataReceivePointByArgument (for Re1) and dataSendPoint (for ReZ) results in the following APIs:

void ReZ( void )
{
Std_ReturnType retValue = RTE_E_OK;
DtSpd1 spd;
/* fill spd with data … */

/* write spd to Port PpTx*/
retValue = Rte_Write_PpTx_speed( spd );
}
void Re1( void )
{
Std_ReturnType retValue = RTE_E_OK;
DtSpd1 spd;
/* read spd from port RpRx*/
retValue = Rte_Read_RpRx_speed( &spd );

}

If the software developer afterwards realizes that they need data from Port RpRx inside Re2 as well, they can simply use the existing API Rte_Read_RpRx_speed that was generated for port access from Re1. Although this would not prompt any additional error message or warning from the compiler during the building process, there are good reasons not to do it. The configured Access Points, for example, are used by the RTE-Generator to detect concurrent data access and to determine whether read and write need to be protected against each other by specific mechanisms.

Diagram 1 shows a constellation where simultaneous data access is not a problem because both the write access from ReZ and the read access from Re1 are running in the same Task A. This means that they cannot be executed concurrently or interrupt each other. In this case, the RTE-Generator is unlikely to provide any additional protection mechanisms such as Interrupt Suspension or OS-Resources. A simple data assignment like the following may be generated for the APIs:

#define Rte_Write_PpTx_speed( data ) (Rte_RxBuf_42 = (data), RTE_E_OK)
#define Rte_Read_RpRx_speed( &data ) (*(data) = Rte_RxBuf_42, RTE_E_OK)

But if Re2 is now also reading the data from Port RpRx, and Re2 is running in a Task B with a higher priority than Task A, then a write access from ReZ may be interrupted by a read access from Re2 and the read data might be inconsistent (in the case that DtSpd1 is not “atomic” data) as shown in diagram 2.

AUTOSAR configuration Timeline

Other problems might occur in similar scenarios, e.g. in the case that ReZ is running in Task C. Here, the RTE-Generator would have to create additional protection mechanisms to prevent concurrent access between Task A (read access in Re1) and Task C (write access in ReZ). But Re2 from Task B may not have access to the generated protections like OS-Resources which will (in the best case) result in an OS_Error call.

For other access types such as dataReadAccess or Client-Server communication, similar problems apply.

In order to fix this problem the configuration of the SWC and its Runnables simply has to be completed with an additional Port Access from Re2 to RpRx as shown in diagram 3.

AUTOSAR configuration 2

Afterwards, all three Runnables can safely access the Ports though the generated APIs:

void ReZ( void )
{
Std_ReturnType retValue = RTE_E_OK;
DtSpd1 spd;
/* Fill spd with data … */

/* write spd to port PpTx */
retValue = Rte_Write_PpTx_speed( spd );
}

void Re1( void )
{
Std_ReturnType retValue = RTE_E_OK;
DtSpd1 spd;
/* read spd from port RpRx */
retValue = Rte_Read_RpRx_speed( &spd );

}

void Re2( void )
{
Std_ReturnType retValue = RTE_E_OK;
DtSpd1 spd;
/* read spd from port RpRx */
retValue = Rte_Read_RpRx_speed( &spd );

}

Compared to the example above, no new API is being created but the generated code behind the APIs is becoming more complex (depending on the RTE-Generator and other configuration parameters):

#define Rte_Write_PpTx_speed( data ) Rte_Write_SWC2_PpTx_speed( data )
#define Rte_Read_RpRx_speed( &data ) Rte_Read_SWC1_RpRx_speed( &data )

FUNC(Std_ReturnType, RTE_CODE) Rte_Write_SWC2_PpTx_speed( DtSpd1 data )
{
Std_ReturnType Rte_Status = RTE_E_OK;
Rte_IntLock();
Rte_RxBuf_42 = (data);
Rte_IntUnlock();
return Rte_Status;
}

FUNC(Std_ReturnType, RTE_CODE) Rte_Read_SWC1_RpRx_speed( P2VAR(DtSpd1, AUTOMATIC, RTE_APPL_DATA) data )
{
Std_ReturnType Rte_Status = RTE_E_OK;
Rte_IntLock();
(*data) = Rte_RxBuf_42;
Rte_IntUnlock();
return Rte_Status;
}

Conclusion: Bypassing the configuration by using the generated APIs for purposes (or Runnables) other than those for which they are configured might cause the data to become inconsistent. Analyzing and fixing such issues can cost a lot of (avoidable) time. Therefore, it is better to configure the system correctly and completely and for code implementation to stick to the intended configuration. In other words, use only those APIs that are also generated for the corresponding Runnable.