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:
- Configure AUTOSAR Classic environment for your application.
- Configure the GHS compiler toolchain and build your environment.
- Integrate and test RH850 board support packages and drivers on your target hardware.
- Generate a Qt Safe layout file from QML as described in Qt Quick Ultralite application with safety-critical items.
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(¤tCount); 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.