RamDisk

Overview

The RamDisk is a general-purpose implementation of a component that offers the if_OS_Storage interface and maps all I/O functionality to an internal buffer in RAM.

By default, the RamDisk storage is empty (i.e. filled with zeros) when initialized. However, it can also be provided with an image file (during compile time) which allows to pre-load it with data, e.g., with a file system.

Usage

This is how the component can be instantiated in the system.

Declaration of the Component in CMake

The RamDisk needs to be instantiated in the CMake file. Besides the component name, an additional parameter with the system configuration must be given. The parameter with the image file is optional.

RamDisk_DeclareCAmkESComponent(
    <NameOfComponent>
    IMAGE
        <ImageFile>
)

The second parameter is optional and allows linking the RamDisk with a disk image. This image is generated with the RamDisk Generator Tool (rdgen) and will be loaded by the RamDisk into its memory upon startup. This way, the RamDisk can be pre-provisioned with some data. In general it is a C source file that consists of the RAMDISK_IMAGE array and the RAMDISK_IMAGE_SIZE variable, however a detailed description of how this tool can be used can be found in the respective section of this handbook.

Instantiation and Configuration in CAmkES

The component is easy to set up with just one client and storage size to be configured.

Declaring the Component

The component is declared as follows:

#include "RamDisk/RamDisk.camkes"
RamDisk_COMPONENT_DEFINE(<NameOfComponent>)

Instantiating and Connecting the Component

The component is instantiated and connected to a user via its if_OS_Storage interface as follows:

assembly {
    composition {
        component <NameOfComponent>   <nameOfInstance>;

        RamDisk_INSTANCE_CONNECT_CLIENT(
            <nameOfInstance>,
            <client>.<storage_rpc>, <client>.<storage_port>
        )
    }
    ...
}

Configuring the Instance

To configure the size of the RamDisk (i.e., the size of its internal, static buffer), the CAmkES configuration attribute must be set. This attribute would typically be set as part of the system configuration:

// main.camkes
assembly {
    composition {
        ...
    }

    configuration {
        <nameOfInstance>.storage_size = <ramDiskSize>;
    }
}

There is no hard limit to the RAM disk size besides the target system’s architectural limits. On 32-bit systems, no more than 4 GiB RAM can be addressed, but some memory is also required for the driver itself. In the end, the amount of memory must fit into the target systems’ RAM together with the rest of the system. The CapDL loader might fail setting up the system if it exceeds the available memory.

Example

In the following example, we instantiate the RamDisk with a size of 5 MiB and then use it with the file system.

Instantiation of the Component in CMake

In the CMake file we set up a wrapper library for the RamDisk, so it finds the config and also includes the RamDisk into the build:

// Interface library so RamDisk can find the system_config.h
project(system_config C)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})

...

// Declare the component
RamDisk_DeclareCAmkESComponent(
    MyRamDisk
)

Configuration of the Component in the ‘main.camkes’ File

In the main.camkes we set the size of the RamDisk’s internal buffer to 5 MiB.

configuration {
    MyRamDisk.storage_size = 5 * 1024 * 1024; // 5 MiB
}

Instantiation and Configuration in CAmkES

In the main CAmkES composition, we instantiate the RamDisk and connect it to its single client.

Declaring the Component

The component is declared as follows:

#include "RamDisk/RamDisk.camkes"
RamDisk_COMPONENT_DEFINE(MyRamDisk)

Instantiating and Connecting the Component

The component is instantiated and connected to a user via its if_OS_Storage interface:

// Instantiate RamDisk
component MyRamDisk myRamDisk;

// Connect interface PROVIDED by RamDisk
RamDisk_INSTANCE_CONNECT_CLIENT(
    myRamDisk,
    ramDiskUser.ramDiskStorage_rpc, ramDiskUser.ramDiskStorage_port
)

Using the Component’s Interfaces in C

The RamDisk can be used by including the camkes.h header and calling the RPC interface in the following way:

// For the CAmkES generated interface
#include <camkes.h>

// For wrapped access to the interface
#include <OS_Dataport.h>
#include <if_OS_Storage.h>

#include "lib_macros/Check.h"

static const if_OS_Storage_t storage =
    IF_OS_STORAGE_ASSIGN(
        myRamDisk_rpc,
        myRamDisk_port);

static const OS_Dataport_t port =
    OS_DATAPORT_ASSIGN(
        myRamDisk_port);

...

int run() {
    const off_t   desiredOffset = 13;
    const size_t  dataSz        = 42;
    const uint8_t writePattern  = 0xA5;

    void* const dataPort = OS_Dataport_getBuf(port);

    CHECK_PTR_NOT_NULL(dataPort);

    // Verify in the production code that there is no data port overflow.
    memset(dataPort, writePattern, dataSz);

    size_t bytesWritten = 0U;
    if(OS_SUCCESS != storage.write(
        desiredOffset,
        dataSz,
        &bytesWritten))
    {
        // Handle the write error.
    }

    if(dataSz != bytesWritten)
    {
        // Handle the write error.
    }

    // Clearing the data port for sanity.
    memset(dataPort, 0xFF, dataSz);

    size_t bytesRead = 0U;
    if(OS_SUCCESS != storage.read(
        desiredOffset,
        dataSz,
        &bytesRead))
    {
        // Handle the read error.
    }

    if(dataSz != bytesRead)
    {
        // Handle the read error.
    }

    // Verifying the read content:
    for(size_t i = 0; i < dataSz; ++i)
    {
        if(writePattern != dataPort[i])
        {
            // Shall never happen!
        }
    }

    ...
}