C

Adapting Output Monitor to AUTOSAR

Overview

This topic provides tips how you can integrate a Complex Device Driver (CDD) for display output verification via Cyclic Redundancy Check (CRC) checks in an AUTOSAR Classic environment that is running on an RH850 microcontroller.

See Qt Quick Ultralite Architecture for more information about the architecture of an application with Qt Safe Renderer and Qt Quick Ultralite.

The example code in this topic demonstrates the following functionality:

  • Interaction with the display-related hardware drivers (DISCOM) to perform CRC operations on specified screen regions.
  • Verification of output states.
  • Communication with other software components using the AUTOSAR Runtime Environment (RTE).

Prerequisites

Set up the following prerequisities on your environment:

Generating Monitor Configuration Files

Use the Qt Safe Monitor Config Tool to generate the C-data structure for the Monitor. The data contains the expected CRC values, states, and positions packed into a C-structure. As an input, the tool takes the generated Qt Safe Renderer layout configurations.

The following commands demonstrate how to generate the Monitor configuration data from the Indicators example:

mkdir data
cd data
C:\Qt\Tools\QSR-2.2.0\bin\qtsafeconfigtool.exe C:\Qt\Examples\QtSafeRenderer-2.2.0\saferenderer\indicators\layoutData\MainForm\MainForm.ui.srl -o . --xml

Building Output Verifier and Monitor Libraries

Build the Output Verifier adaptation and the Monitor library from the Src/QtSafeRenderer-runtime-2.2.0 folder.

The Output Verifier adaptation library is located in src/adaptation/outputverifier/autosar. Build it as follows:

make -f Makefile_gcc all

This command generates the liboutputverifier.a archive.

The Monitor library is located in the src/monitor folder. Build it as follows:

make -f Makefile_gcc all

This command generates the libmonitor.a archive.

Integration Steps

You find the basic project structure for the AUTOSAR adaptation in Src/QtSafeRenderer-runtime-2.2.0/test/manual/qul-monitor folder. The project includes the project makefile, stub implementation for the hardware adaptation, hooks for the Qt Safe Renderer output verifier, and monitor modules.

Receiving Messages from the AUTOSAR Runtime Environment

The following example code demonstrates receiving and processing system state change events from the AUTOSAR Runtime Environment (RTE).

When your system receives a safe item event, it is added to the verification queue using the add_item_to_verification helper function. You can find the function in qsrMonitor.c file located within the tests/manual/qul-monitor project.

// Code to receive and handle system state change events from the RTE
SafeMessage incomingMessage;
Std_ReturnType returnValue = Cdd_Qsr_ReceiveMessage(incomingMessage);

while ((returnValue & RTE_E_NO_DATA) == 0) {
    // Handle different return values
    if (returnValue == RTE_E_OK) {
        uint32 messageId = *(uint32*)incomingMessage;

        if (messageId == EventSystemStateChange) {
            struct ChangeStateEvent* incomingChangeStateEvent = (struct ChangeStateEvent*)&incomingMessage;
            uint32_t QsrItemId = 0;

            // Map CDD item ID to QSR item ID and add to verification queue.
            if (Cdd_Qsr_CddItemIdToQsrItemId((ItemId)incomingChangeStateEvent->itemId, &QsrItemId) == 0) {
                add_item_to_verification(QsrItemId, incomingChangeStateEvent->value);
            }

            // Forward the event to QUL for rendering
            returnValue = Rte_Send_Qsr_Qul_Message_Queue_Message(incomingMessage);
            Cdd_Qsr_Assert(returnValue == RTE_E_OK);
        }
    } else if (returnValue & RTE_E_LOST_DATA) {
        // Log error or take corrective action when data is lost
        // Error handling code can include logging the error or retrying message retrieval
    }

    // Attempt to receive the next message
    returnValue = Cdd_Qsr_ReceiveMessage(incomingMessage);
}

Generating Qt Safe Renderer Item IDs from QML Identifiers

Monitor uses safe item IDs as identifiers. You can calculate these IDs by using the qsafe_hash function, which takes the QML item ID as a parameter.

The following code demonstrates a function for mapping between the Complex Device Driver (CDD) item ID and Qt Safe Renderer item ID:

static int Cdd_Qsr_CddItemIdToQsrItemId(ItemId cddItemId, uint32_t *qsrItemId) {
    if (!qsrItemId) {
        return 1;  // Error handling: invalid pointer
    }

    const char *qmlId = NULL;

    // Mapping QML IDs to CDD Item IDs
    switch (cddItemId) {
    case ItemId_Blinkers_Left:
        qmlId = "leftBlinker";
        break;
    case ItemId_Blinkers_Right:
        qmlId = "rightBlinker";
        break;
    case ItemId_Lights_Low_Beam:
        qmlId = "lowBeamLight";
        break;
    default:
        return 1;  // Error handling: unknown item
    }

    size_t length = strlen(qmlId);
    const qchar *qmlIdQchar = (const qchar *)qmlId;
    *qsrItemId = qsafe_hash(qmlIdQchar, length);
    return 0;
}

Handling the Heartbeat Event

The Qt Quick Ultralite application sends periodically a heartbeat event to the monitor process. The following code demonstrates how you receive the heartbeat and handle the timeout:

// Variables to track heartbeat
static uint64 lastHeartbeatUpdate = 0;
const uint64 heartbeatCheckPeriod = 2000; // Time threshold for heartbeat timeout

uint64 timestamp = Cdd_Qsr_Timestamp();

// ... Code for receiving and processing other messages ...

// Receive heartbeat message from QUL CDD
Std_ReturnType returnValue = Rte_Receive_Qul_Qsr_Message_Queue_Message(incomingMessage);
if (returnValue == RTE_E_OK) {
    uint32 messageId = *(uint32*)incomingMessage;

    if (messageId == EventHeartbeatUpdate) {
        // Update the last known heartbeat timestamp
        lastHeartbeatUpdate = timestamp;
    }
} else if (returnValue & RTE_E_LOST_DATA) {
    // Handle message loss error
    Cdd_Qsr_Assert(0);
}

// After processing messages, check if heartbeat timed out
if (timestamp > lastHeartbeatUpdate + heartbeatCheckPeriod) {
    // No heartbeat received within the allowed period
    // Take appropriate action, such as setting a reboot flag
    reboot = 1;
}

// If no reboot is needed, reset the watchdog timer to keep the system running
if (!reboot) {
    Cdd_Qsr_Restart_Watchdog_Timer();
}

You get the timestamp information as follows:

static uint64 Cdd_Qsr_Timestamp(void)
{
    static TimeInMicrosecondsType prevCount = 0;
    static uint64 timestampMilliseconds = 0;

    TimeInMicrosecondsType currentCount;
    Std_ReturnType returnValue = Rte_Call_Os_Service_GetCounterValue(&currentCount);

    if (returnValue == RTE_E_Os_Service_E_OK)
    {
        uint32 delta;

        if (prevCount <= currentCount)
        {
            delta = currentCount - prevCount;
        }
        else
        {
            delta = OSMAXALLOWEDVALUE_SystemTimer - prevCount + currentCount;
        }

        prevCount = currentCount;

        // OSTICKDURATION_SystemTimer is defined as nanoseconds per tick
        uint64 deltaNanoseconds = (uint64)delta * OSTICKDURATION_SystemTimer;
        timestampMilliseconds += deltaNanoseconds / 1000000;
    }

    return timestampMilliseconds;
}

Implementing OutputVerifier Module Callbacks in the CDD

You need to implement the OutputVerifier module callback functions in your CDD code for your specific hardware. The following example illustrates the implementation for RH850 hardware.

You can find the header file in the directory Src/QtSafeRenderer-runtime-2.2.0/src/adaptation/outputverifier/autosar/outputverifier_capi.h

Requesting CRC Calculation for a Specified Region

The following function initiates a CRC calculation for a specific display region. It is used within the AUTOSAR adaptation to ensure the integrity of the rendered content in that region.

int32_t Cdd_Qsr_Request_Crc(uint32_t unit,
                            uint16_t x,
                            uint16_t y,
                            uint16_t width,
                            uint16_t height);

Parameters

  • unit: The CRC unit number to be used for the calculation. This should correspond to one of the pre-configured CRC hardware units.
  • x: The x-coordinate of the upper left corner of the rectangular region on the display.
  • y: The y-coordinate of the upper left corner of the rectangular region.
  • width: The width of the rectangular region.
  • height: The height of the rectangular region.

Return value

  • 0: Indicates that the CRC calculation request was successful.
  • 1: Indicates an error in the parameters or in initiating the CRC calculation.

Description

This function requests a CRC calculation for a designated region of the display. It sends a command to the Discom CRC hardware unit to compute the CRC value for the pixels within the defined rectangle.

Reference Implementation

int32_t Cdd_Qsr_Request_Crc(uint32_t unit, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
    // Validate unit index against the number of available CRC units
    if (unit >= discomUnitCount) {
        return 1; // Error code indicating invalid unit index
    }

    // Check for boundary conditions on display coordinates and dimensions
    if (x > 1279 || y > 1023 || width == 0 || width > 1280 || height == 0 || height > 1024) {
        return 1; // Error code indicating parameter values are out of allowable range
    }

    // Setup CRC calculation parameters
    discom_unit_t *discom = &discomUnits[unit];
    Cdd_Qsr_Assert(discom->status == discom_idle);
    discom->status = discom_crc_calculation;

    r_discom_Param_t *param = &discom->param;
    param->HOffset = x;
    param->VOffset = y;
    param->HSize = width;
    param->VSize = height;
    param->AlphaMode = R_DISCOM_ALPHA_DEFAULT;
    param->Alpha = 0xFF;

    if (R_DISCOM_ParamSet(unit, &discom->param) != R_DISCOM_ERR_OK) {
        return 1;
    }

    // RH850_D1x_Manuals_V151/r01uh0451ej0220_rh850d1lm.pdf:
    // The operation starts at the next frame after the CMPR bit in DOCMCR is set to 1.
    // The CMPR bit in DOCMCR is loaded inside upon detection of the start of the valid period of the graphics data.
    if (R_DISCOM_Enable(unit) != R_DISCOM_ERR_OK) {
        return 1;
    }
    return 0; // Success
}

Waiting for Results and Reading CRC

To ensure that the display is stable and updated before reading CRC, the code waits for multiple V-Blank cycles as follows:

void Cdd_Qsr_Wait_for_Result(void)
{
    Cdd_Qsr_Wait_For_Vblank();
    Cdd_Qsr_Wait_For_Vblank();
}

Reading the CRC Value

After waiting for the CRC calculation to complete, the CRC value can be read using the Cdd_Qsr_Read_Crc function, which fetches the CRC value from the specified hardware unit.

uint32_t Cdd_Qsr_Read_Crc(uint32_t unit);

Parameter

The CRC unit to be read.

Returns

The computed CRC value.

Reference Implementation

uint32_t Cdd_Qsr_Read_Crc(uint32_t unit)
{
    // Ensure that unit index is valid.
    Cdd_Qsr_Assert(unit < discomUnitCount);
    // Ensure CRC operation completed successfully
    Cdd_Qsr_Assert(R_DISCOM_Disable(unit) == R_DISCOM_ERR_OK);
    uint32_t crc = 0;
    Cdd_Qsr_Assert(R_DISCOM_CrcGet(unit, &crc) == R_DISCOM_ERR_OK);
    return crc;
}

Handling CRC Verification Errors

The Monitor calls the errorFunc callback when verification fails. The following code in Src/QtSafeRenderer-runtime-2.2.0/test/manual/qul-monitor/qsrMonitor.c illustrates how to implement the verify_output functionality in the Monitor.

void verify_output(errorFunc errorHandler)
{
    uint32_t i=0U;
    RegionDefinition result;

    OutputVerifier_verifyOutput();
    for (; i < OutputVerifier_resultsCount(); i++)
    {
        result = OutputVerifier_getVerificationResult();
        if (verifyCRC(result.id, result.state, result.crc) != 0) {
            errorHandler(CRCVerificationFailed, (int64_t)result.id, (int64_t)result.crc);
            break;
        }
    }
}

The following function, located in Src/QtSafeRenderer-runtime-2.2.0/test/man ual/qul-monitor/test.c implements the default error handling functionality. In this example, it prints the failing item along with the expected and actual CRC values. You can adapt this section in the production code to implement the desired error handling logic.

static void defaultErrorHandler(ErrorCode errorType, int64_t arg1, int64_t arg2)
{
    printf("Error: %s\n", errorCodeToString(errorType));
    printf("Item id: %d, CRC checking failed. Actual value: %x\n", (uint32_t)arg1, (uint32_t)arg2);
}

Available under certain Qt licenses.
Find out more.