RTA Knowledge Base

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Content

Table of Contents
outlinetrue

Introduction

Relevant AUTOSAR Documents

  • Specification of NVRAM Manager (AUTOSAR_SWS_NVRAMManager)
  • NV Data Handling Guideline (AUTOSAR_EXP_NVDataHandling)
  • Specification of RTE Software (AUTOSAR_SWS_RTE)

History

The specification for the BSW NVM module has existed since AUTOSAR Classic 1.0, but the additions to the RTE specification for handling NV data were added to the standard in version 4.0 (2009).

Terminology

When we refer to NVRAM we are referring to the concept of Non-Volatile Memory, i.e. memory which is not lost during a power cycle (typically stored in flash or EEPROM).

Background

Like any other software system, AUTOSAR platforms are likely to require storage which persists across power cycles.

As per most AUTOSAR concepts, the reading and writing of NV data from the application level follows a chain of abstractions, starting at the RTE and following down through the BSW software stack, including (in order):

  • The NvM Module (NvM)
    • Defined in AUTOSAR_SWS_NVRAMManager
  • The Memory Abstraction Interface (MemIf)
    • Defined in AUTOSAR_SWS_MemoryAbstractionInterface
  • Memory Abstraction Models
    • Flash EEPROM Emulation (FEE)
      • Defined in AUTOSAR_SWS_FlashEEPROMEmulation
      EEPROM Abstraction (EA)
      • Defined in AUTOSAR_SWS_EEPROMAbstraction
  • Drivers
    • Flash Driver (FLS)
      • Defined in AUTOSAR_SWS_FlashDriver
      EEPROM Driver (EEP)
      • Defined in AUTOSAR_SWS_EEPROMDriver

This document will only cover the abstraction of NV data at the level of the RTE and NvM module.

Intent

At its core, the NVRAM specification covers three very simple behaviours:

  • Writing data stored in an application's RAM to NV storage.
  • Populating variables in RAM from values in NV storage.
  • Filling variables in RAM with default values stored in ROM or calculated by application code if something goes wrong when retrieving a persistent variable from NV storage.

There are multiple permutations of this behaviour in the specification but they mostly revolve around two factors.

Memory Management and RAM Blocks

Who is responsible for managing the memory containing the in-RAM representation of the data?

All NV blocks are simply storage for variables which are resident in RAM (known as RAM blocks), so all NV blocks must have somewhere in RAM where their current status can be stored during runtime. The NVM specification provides multiple ways to manage the memory used for storing runtime versions of NV data:

  • An application SWC can handle the RAM area itself by using some of it's RAM space.
  • An application SWC can use the RTE to access an element of PerInstanceMemory for holding it's RAM copy of NV data.
  • An NvBlockSwComponentType can be created which internalises the management of RAM blocks.

The NVM specification makes a distinction between temporary RAM blocks and permanent RAM blocks. Temporary blocks are made on the fly and sent directly to the NvM module for writing at runtime whereas a permanent block will be defined ahead of time (typically as a PIM or a RAM block in an NV SWC). Different APIs are used to indicate writes or reads of temporary blocks (NvM_WriteBlock or NvM_ReadBlock) versus permanent blocks (NvM_WritePRAMBlock or NvM_ReadPRAMBlock).

Depending on how the system is configured, this also has an impact on the semantics which can be used for writing back the relevant RAM data to NV storage.

NV Block Writing Semantics

Applications can either write their RAM values to NV storage using implicit semantics or explicit semantics. Note that this is not the same as implicit and explicit communication semantics in the RTE!

When the NvM module is called to write to NV storage the write does not take place immediately and only takes place when the NvM module's main function is next called by the scheduler. The different semantics here have an impact on what is required of the application making the write in this period.

  • Implicit writes to NV storage do not have a dedicated RAM buffer between the in-RAM representation and the NvM module. When a write is requested, the data from the RAM block is sent to the NvM module as part of the call, so the relevant RAM block must be protected from writing by the application SWC until the NvM module reports that the write to NV is complete.
  • Explicit writes take place when a second RAM block is present (known as a RAM mirror). In this case, when the NvM module is scheduled and makes the write, it simply copies the contents of the RAM block to the RAM mirror and writes the contents of the mirror at the time the copy took place. This allows the application to continue writing the RAM block until the NvM module makes the copy.

Management of Calls to NvM

How are requests to write data sent to the NvM module?

As part of the specification, the user can identify which part of the system is responsible for managing when writeback from RAM blocks to NV blocks takes place. This takes three forms:

  • Application SWCs are responsible for requesting writes from the NvM module. This is achieved through client/server connections between the SWC and the NvM module (as a ServiceSwComponent), where the SWC acts as a client to request services from the NvM (such as writes/reads) and the SWC acts as a server to receive notifications (such as write's having completed) from the NvM.
  • An NV SWC is responsible for requesting writes from the NvM module in response to a combination of writes on an NvDataInterface and client/server calls. In this case, the data to be written to a RAM block is sent to the NV SWC using a sender/receiver interface. The connected application SWC then indicates to the NV SWC that a write to the NV block must take place using a client/server interface.
  • The RTE is directly responsible for invoking the NvM APIs directly to write data at certain points based on the configuration of the dirtyFlag and NvBlockNeeds. In this case, for example, an NV block may have it's dirtyFlag set to TRUE and be using the parameter writeImmediate. In this case, no client/server calls are involved and the RTE calls the NvM write APIs immediately when data is received on the relevant NvDataInterface.

Note: From the perspective of the RTE developer there is heavy overlap between the first and second use cases, as the RTE is likely to generate very similar code to represent client/server connections between either an NV SWC or directly to the NvM module.

ROM Blocks

The specification also contains details on ROM blocks. A ROM block represents a fallback option which can populate a RAM block in the case that an NV block is corrupted or fails to be accessed for some reason. In this case, the RAM block will be populated with the data from the ROM block. Alternatively, the parameter NvMInitBlockCallback can be used to provide callbacks which allow default values for RAM blocks to be created on the fly.

Handling Reads

Data from the NV storage only needs to be read once to initialise the contents of the relevant RAM blocks. This typically takes place in two places:

  • If an application SWC is managing it's own temporary RAM block it can call the NvM module via client/server interfaces to request a read of the backing NV block.
  • Any permanent RAM blocks will be initialised on startup by the NvM module via calls to the RTE callback Rte_SetMirror.

System Configuration

The NvBlockSwComponentType

As mentioned above, NV blocks can be represented in the Application Layer by an NvBlockSwComponentType. An NV SWC contains a number of NvBlockDescriptors which describe a RAM block and a corresponding ROM block.

All NvBlockDescriptors in the NV SWC come together to make up an NVRAM Block, which is represented in the NvM module as an NvMBlock.

When using an NV SWC, the explicit synchronisation strategy is typically used, causing the NvMBlock in the NvM module to have a single RAM mirror used as a 'buffer' between reads and writes to RAM blocks in the NV SWC and the relevant backing NV blocks.

The dirtyFlag Parameter

When a RAM block in an NvBlockDescriptor is written, the modified value must be written back to the backing NV block which is being mirrored.

Each NvBlockDescriptor contains the parameter supportDirtyFlag. This flag indicates how writeback of altered RAM blocks to their backing NV blocks is managed by an NV SWC.

When this flag is set to FALSE, the NV SWC must use Client/Server interfaces to the NvM module to manage reading and writing NV blocks (such as Rte_Call_<PORT>_ReadRamBlockFromNvM or Rte_Call_<PORT>_WriteRamBlockToNvM).

When this flag is set to TRUE, the NV SWC may call the NvM directly to handling reading and writing of NV blocks (using NvM_WriteBlock and NvM_ReadBlock).

NvBlockNeeds

Each NvBlockDescriptor must reference an NvBlockNeeds structure, which defines information about the requirements and constraints on writing and reading from the RAM block defined in that descriptor. Many parameters can be configured in an NvBlockNeeds, including:

  • storeAtShutdown
    • Indicates that the RAM block of this descriptor must be stored on shutdown.
  • storeCyclic
    • Indicates the that RAM block of this descriptor should be periodically stored in the backing NV block.
  • storeImmediate
    • Indicates that the RAM block of this descriptor must be immediately flushed to the backing NV block on write.

Rte_GetMirror and Rte_SetMirror Callbacks

In the explicit synchronisation strategy used with NV SWCs, the NvM module periodically has to request access to the RAM blocks contained within an NV SWC, either to fill them with data from the relevant backing NV block or to retrieve the state of the mirror to write it to the backing NV block.

The Rte_GetMirror_<b>_<d> and Rte_SetMirror_<b>_<d> callbacks are generated by the RTE to support this mechanism (where <b> is the name of the NvBlockSwComponentType's prototype and <d> is the name of the NvBlockDescriptor).

Rte_GetMirror is called by the NvM module to retrieve the contents of the mirrored RAM block held by an NV SWC. This is used to copy the contents of the RAM block to the NvM's internal RAM block before copying it to it's backing NV block.

Rte_SetMirror is called by the NvM module to write the contents of a backing NV block to it's RAM block mirror held in an NV SWC.

Accessing NV SWC Data

Reading and writing data from/to NVRAM from a software component uses a special interface; the NvDataInterface. Like a SenderReceiverInterface, this interface defines a number of VariableDataPrototypes which are to be exchanged between an NV SWC and other types of SWCs.

VDPs in an NV interface can be referenced by application SWCs via a DataWriteAccess, DataReadAccess, DataReceivePoint or DataSendPoint (by value or by reference) in the same way as VDPs in S/R interfaces (which defines whether the access sematics are implicit or explicit).

NV data can be accessed using both explicit and implicit semantics. In both scenarios, behaviour differs based on whether the dirty flag is used.

Runtime Behaviour

RAM block initialisation

RAM blocks stored in an NV SWC must be initialised to their last known values on system boot up. The RTE recieves calls to the relevant Rte_SetMirror APIs from the NvM module when it is started by the BswM.

NV Writes

When application SWCs write to an NV SWC via an NV interface, the runtime behaviour depends on the supportDirtyFlag parameter in the System configuration.

dirtyFlag = FALSE

If the dirty flag is set to FALSE, the RTE will not handle all elements of the write to the backing NV block.

In this case, the following behaviour occurs:

  • The writing SWC calls the relevant Rte_Write or Rte_IWrite API to write to the NV SWC.
    • The RTE code then writes to the relevant RAM block representing the NvBlockDescriptor, either immediately in the explicit case or after the runnable has finished executing as part of buffer flushes in the implicit case.
  • The writing SWC then calls the Rte_Call_NvM_Service_WriteBlock API (which should be generated by the RTE as a result of connecting a client/server interface between the writing SWC and the NvM module).
    • This API calls the NvM_WriteBlock API provided by the NvM module.
  • When the NvM module is next scheduled, it calls the relevant Rte_GetMirror API to retrieve the contents of the RAM mirror stored by the NV SWC in step 1 and passes it further down the stack to be written to the NV block.
  • When completed, the NvM module calls the relevant Rte_NvMNotifyJobFinished API. As the writing SWC is configured with a client/server connection to the NvM module to be triggered by this event, this causes the RTE to trigger an OperationInvokedEvent in the writing SWC.

dirtyFlag = TRUE

If the dirty flag is set to TRUE, the RTE's behaviour differs based on the semantics in the relevant NvBlockNeeds.

The simplest scenario is the use of storeImmediate semantics:

  • The writing SWC calls the relevant Rte_Write or Rte_IWrite API to write to the NV SWC.
    • The RTE code then writes to the relevant RAM block representing the NvBlockDescriptor, either immediately in the explicit case or after the runnable has finished executing as part of buffer flushes in the implicit case.
  • The RTE calls the NvM_WriteBlock API directly to trigger writing to the backing NV block.
  • When the NvM module is next scheduled, it calls the relevant Rte_GetMirror API to retrieve the contents of the RAM mirror stored by the NV SWC in step 1 and passes it further down the stack to be written to the NV block.
  • When completed, the NvM module calls the relevant Rte_NvMNotifyJobFinished API, but this triggers no calls to the writing SWC.

The behaviour of storeCyclic semantics is largely the same, except that the RTE will not directly call NvM_WriteBlock within the RteWrite API, but rather will raise a flag to an RTE owned runnable which is triggered by a cyclic timing event which then calls the NvMWriteBlock API.

With storeShutdown semantics, the Rte_Write API calls the NvM_SetRamBlockStatus API to indicate that the RAM block needs to be writte, and the BswM will call NvM_WriteAll at shutdown time to indicate that the NV block needs to be written.

Configuration Examples

Implementation of NV SWC Client/Server Connections

When the input model contains an NV SWC, RTA-RTE will generate the relevant code to support calls to the NvM module through client/server connections to the NV SWC.

RTA-RTE relies on the user of RoleBasedPortAssignments to indicate which ports on the NV SWC represent which parts of the client/server API to the NvM module, such as:

<NV-BLOCK-DESCRIPTORS>
    <NV-BLOCK-DESCRIPTOR>
        <SHORT-NAME>block1</SHORT-NAME>
        <CLIENT-SERVER-PORTS>
            <ROLE-BASED-PORT-ASSIGNMENT>
                <PORT-PROTOTYPE-REF DEST='R-PORT-PROTOTYPE'>/TNvRamCS/swc_nvblock1/client3</PORT-PROTOTYPE-REF>
                <ROLE>NvMService</ROLE>
            </ROLE-BASED-PORT-ASSIGNMENT>
        </CLIENT-SERVER-PORTS>

In this case, the port client3 is mapped to the NvMService role to indicate that it maps to the NvMService interface on the NvM module.

RTA-RTE then generates the relevant calls to the NvM module based on OperationInvokedEvents mapped to these ports. For example, in this configuration, an OIE exists which maps to the ReadBlock operation of the NvMService interface:

<OPERATION-INVOKED-EVENT>
    <SHORT-NAME>op2s</SHORT-NAME>
    <START-ON-EVENT-REF DEST='RUNNABLE-ENTITY'>/TNvRamCS/swc_nvblock1/IBswc_nvblock1/re_read</START-ON-EVENT-REF>
    <OPERATION-IREF>
        <CONTEXT-P-PORT-REF DEST='P-PORT-PROTOTYPE'>/TNvRamCS/swc_nvblock1/server</CONTEXT-P-PORT-REF>
        <TARGET-PROVIDED-OPERATION-REF DEST='CLIENT-SERVER-OPERATION'>/TNvRamCS/ifNvMService/ReadBlock</TARGET-PROVIDED-OPERATION-REF>
    </OPERATION-IREF>
</OPERATION-INVOKED-EVENT>

This causes the RTE to generate the implementation of the relevant Rte_Call APIs which call the NvM module:

FUNC(Std_ReturnType, RTE_CODE)
    Rte_Call_swc_app_clientA_ReadBlock(VAR(PUInt16, AUTOMATIC) dstPtr) /* 1 */
    {
       VAR(Std_ReturnType, AUTOMATIC) rtn;

       /* Box: generateImplementation_SyncIntraTask_Body() - C:\development\RTA-RTE21\trunk\RTEGen\Libraries\Client\PrimaryPortInterface.cpp( 1300 )  begin */
       /* Parameter dstPtr has direction IN */
       NvM_ReadBlock(((VAR(SInt16, AUTOMATIC))18), dstPtr);
       rtn = ((VAR(Std_ReturnType, AUTOMATIC))RTE_E_OK);
       /* Box: generateImplementation_SyncIntraTask_Body() - C:\development\RTA-RTE21\trunk\RTEGen\Libraries\Client\PrimaryPortInterface.cpp( 1300 )  end */

       return rtn;
    }

GetMirror/SetMirror Callbacks

RTA-RTE also generates implementations of the relevant Rte_GetMirror and Rte_SetMirror callbacks as required by the NvM module:

FUNC(Std_ReturnType, RTE_CODE)
    Rte_GetMirror_nv1_block1(P2VAR(void, AUTOMATIC, RTE_DATA) NVMBuffer) /* 1 */
    {
       VAR(Std_ReturnType, AUTOMATIC) rtn = RTE_E_OK;

       rtn = ((VAR(StatusType, AUTOMATIC))E_OK);
       Rte_SuspendOSInterrupts();
       Rte_memcpy(NVMBuffer, &Rte_RamBlk_swc_nvblock1_block1_myRamBlock1, sizeof(UInt16));
       Rte_ResumeOSInterrupts();
       return rtn;
    }

FUNC(Std_ReturnType, RTE_CODE)
    Rte_SetMirror_nv1_block1(P2CONST(void, AUTOMATIC, RTE_DATA) NVMBuffer) /* 1 */
    {
       VAR(Std_ReturnType, AUTOMATIC) rtn = RTE_E_OK;

       rtn = ((VAR(StatusType, AUTOMATIC))E_OK);
       Rte_SuspendOSInterrupts();
       Rte_memcpy(&Rte_RamBlk_swc_nvblock1_block1_myRamBlock1, NVMBuffer, sizeof(UInt16));
       Rte_ResumeOSInterrupts();
       return rtn;
    }

Implementation of dirtyFlag Semantics

RTA-RTE supports generation of code to support the semantics for storeImmediate, storeCyclic and storeAtShutdown.

storeImmediate

In the case that a NvDataInterface contains a VDP which is part of an NvBlockDescriptor which contains an NvBlockNeeds in which the storeImmediate parameter is set to true, the user must configure a DataReceivedEvent in a specific way to allow RTA-RTE to auto generate a runnable which handles the writing of data.

For example:

<DATA-RECEIVED-EVENT>
    <SHORT-NAME>immediateEvent</SHORT-NAME>
    <DISABLED-MODE-IREFS/>
    <START-ON-EVENT-REF DEST='RUNNABLE-ENTITY'>/TEfficientNVRunnableEntityCheck/NvBlockSwComponentTypes/nvSWC/IBnvSWC/reImmediate</START-ON-EVENT-REF>
    <DATA-IREF>
        <CONTEXT-R-PORT-REF DEST='R-PORT-PROTOTYPE'>/TEfficientNVRunnableEntityCheck/NvBlockSwComponentTypes/nvSWC/nvSWCRPortImmediate</CONTEXT-R-PORT-REF>
        <TARGET-DATA-ELEMENT-REF DEST='VARIABLE-DATA-PROTOTYPE'>/TEfficientNVRunnableEntityCheck/NvDataInterfaces/nvDataIF/portsVDProto</TARGET-DATA-ELEMENT-REF>
    </DATA-IREF>
</DATA-RECEIVED-EVENT>

This results in the generation of a runnable entity to implement the writeImmediate semantics:

FUNC(void, nvSWC_CODE)
    nvswc_re_immediate(void) /* 1 */
    {
       if ( TRUE == Rte_ramBlockStoreImmediate_DirtyFlag )
       {
          (void)NvM_WriteBlock(NvMConf_NvMBlockDescriptor_NvMBlockDescriptorImmediate, &Rte_RamBlk_nvSWC_ramBlockImmediateDescriptor_ramBlockStoreImmediate);
          Rte_ramBlockStoreImmediate_DirtyFlag = FALSE;
       }
    }

storeCyclic

Configuration of a TimingEvent referencing the NVSWC RunnableEntity

<TIMING-EVENT-REF DEST='TIMING-EVENT'>/TEfficientNVRunnableEntityCheck/NvBlockSwComponentTypes/nvSWC/IBnvSWC/cyclicEvent</TIMING-EVENT-REF>
<TIMING-EVENT>
    <SHORT-NAME>cyclicEvent</SHORT-NAME>
    <DISABLED-MODE-IREFS/>
    <START-ON-EVENT-REF DEST='RUNNABLE-ENTITY'>/TEfficientNVRunnableEntityCheck/NvBlockSwComponentTypes/nvSWC/IBnvSWC/reCyclic</START-ON-EVENT-REF>
    <PERIOD>0.1</PERIOD>
</TIMING-EVENT>

causes the generation of the runnable which implements the behaviour:

FUNC(void, nvSWC_CODE)
    nvswc_re_cyclic(void) /* 1 */
    {
       if ( TRUE == Rte_ramBlockStoreCyclic_DirtyFlag )
       {
          (void)NvM_WriteBlock(NvMConf_NvMBlockDescriptor_NvMBlockDescriptorCyclic, &Rte_RamBlk_nvSWC_ramBlockCyclicDescriptor_ramBlockStoreCyclic);
          Rte_ramBlockStoreCyclic_DirtyFlag = FALSE;
       }
    }

storeAtShutdown

In this case, the RTE does not have to implement a runnable to handle the semantics, but instead notifies the NvRamManager via a call to NvMSetRamBlockStatus as shown below in an example implementation of 'Rte_ImplWrite:

/* Box: dirtyFlagSemanticsSetRamBlockAPIBox begin */
/* Mark the RamBlock as dirty by setting its status as such via NvM since it has storeAtShutdown semantics. */
(void)NvM_SetRamBlockStatus(NvMConf_NvMBlockDescriptor_NvMBlockDescriptorShutdown, TRUE);
/* Box: dirtyFlagSemanticsSetRamBlockAPIBox end */