This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM62P: Send an interrupt message for DSS to Linux via IPC

Part Number: AM62P

Hello experts

 In both ipc_rpmsg_echo.c and dss_display_share.c, we use the same environment variable, but they are two independent programs, and global variables cannot be shared between them, causing a compilation error.

 Linking: am62px:wkup-r5fss0-0:freertos:ti-arm-clang ipc_rpmsg_echo_linux.release.out ...

 undefined           first referenced
  symbol                 in file
 ---------           ----------------
 gSafetyCheckEnabled obj/release/ipc_rpmsg_echo.obj

error: unresolved symbols remain
error: errors encountered during linking; "ipc_rpmsg_echo_linux.release.out"
   not built
tiarmclang: error: tiarmlnk command failed with exit code 1 (use -v to see invocation)
gmake: *** [makefile:200: ipc_rpmsg_echo_linux.release.out] Error 1


How should we go about resolving this error?

  • The above is expected. If you don't have an symbol defined in the project, the compiler will throw undefined symbol error.

    Are the above IPC and DSS files part of same project?

  • They should be two independent projects because they can be compiled separately, but dss_display_share.c calls functions from the IPC project.

    /*
     *  Copyright (C) 2023-2025 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <stdlib.h>
    #include <kernel/dpl/DebugP.h>
    #include <kernel/dpl/ClockP.h>
    #include "ti_drivers_config.h"
    #include "ti_board_config.h"
    #include "ti_drivers_open_close.h"
    #include "ti_board_open_close.h"
    #include "FreeRTOS.h"
    #include "task.h"
    #include <drivers/device_manager/sciserver/sciserver_init.h>
    
    #define TASK_PRI_MAIN_THREAD  (configMAX_PRIORITIES-1)
    #define TASK_PRI_BOOT_THREAD  (configMAX_PRIORITIES-1)
    #define TASK_PRI_IPC_THREAD   (configMAX_PRIORITIES-1)
    
    
    #define TASK_SIZE (16384U/sizeof(configSTACK_DEPTH_TYPE))
    #define TASK_BOOTLOADER_SIZE    (8192U)
    
    StackType_t gMainTaskStack[TASK_SIZE] __attribute__((aligned(32)));
    StaticTask_t gMainTaskObj;
    TaskHandle_t gMainTask;
    DM_LPMData_t gDMLPMData __attribute__((section(".lpm_data"), aligned(4)));
    
    StackType_t gBootTaskStack[TASK_BOOTLOADER_SIZE] __attribute__((aligned(32)));
    StaticTask_t gBootTaskObj;
    TaskHandle_t gBootTask;
    
    StackType_t gIPCTaskStack[TASK_SIZE] __attribute__((aligned(32)));
    StaticTask_t gIPCTaskObj;
    TaskHandle_t gIPCTask;
    
    void dss_display_share_main(void *args);
    void sbl_stage2_main(void *args);
    void ipc_rpmsg_echo_main(void *args);
    void ipc_rpmsg_thread(void *args)
    {
        ipc_rpmsg_echo_main(NULL);
    
        vTaskDelete(NULL);
    }
    
    void main_thread(void *args)
    {
        int32_t status = SystemP_SUCCESS;
    
        /* Open drivers */
        Drivers_open();
        /* Open flash and board drivers */
        status = Board_driversOpen();
        DebugP_assert(status==SystemP_SUCCESS);
    
        /* Init LPM specific data */
        Sciclient_initDeviceManagerLPMData(&gDMLPMData);
    
        sciServer_init();
    
        dss_display_share_main(NULL);
    
        gIPCTask = xTaskCreateStatic( ipc_rpmsg_thread,   /* Pointer to the function that implements the task. */
                                      "ipc_rpmsg_thread", /* Text name for the task.  This is to facilitate debugging only. */
                                      TASK_SIZE,  /* Stack depth in units of StackType_t typically uint32_t on 32b CPUs */
                                      NULL,            /* We are not using the task parameter. */
                                      TASK_PRI_IPC_THREAD,   /* task priority, 0 is lowest priority, configMAX_PRIORITIES-1 is highest */
                                      gIPCTaskStack,  /* pointer to stack base */
                                      &gIPCTaskObj ); /* pointer to statically allocated task object memory */
        configASSERT(gIPCTask != NULL);
    
        /* Close board and flash drivers */
        Board_driversClose();
        /* Close drivers */
        Drivers_close();
    
        vTaskDelete(NULL);
    }
    
    
    int main()
    {
        Bootloader_profileReset();
    
    
        /* init SOC specific modules */
        System_init();
        Bootloader_profileAddProfilePoint("System_init");
        Board_init();
        Bootloader_profileAddProfilePoint("Board_init");
    
        gMainTask = xTaskCreateStatic( main_thread,   /* Pointer to the function that implements the task. */
                                      "main_thread", /* Text name for the task.  This is to facilitate debugging only. */
                                      TASK_SIZE,  /* Stack depth in units of StackType_t typically uint32_t on 32b CPUs */
                                      NULL,            /* We are not using the task parameter. */
                                      TASK_PRI_MAIN_THREAD,   /* task priority, 0 is lowest priority, configMAX_PRIORITIES-1 is highest */
                                      gMainTaskStack,  /* pointer to stack base */
                                      &gMainTaskObj ); /* pointer to statically allocated task object memory */
        configASSERT(gMainTask != NULL);
    
        gBootTask = xTaskCreateStatic( sbl_stage2_main,   /* Pointer to the function that implements the task. */
                                      "boot_thread", /* Text name for the task.  This is to facilitate debugging only. */
                                      TASK_SIZE,  /* Stack depth in units of StackType_t typically uint32_t on 32b CPUs */
                                      NULL,            /* We are not using the task parameter. */
                                      TASK_PRI_BOOT_THREAD,   /* task priority, 0 is lowest priority, configMAX_PRIORITIES-1 is highest */
                                      gBootTaskStack,  /* pointer to stack base */
                                      &gBootTaskObj ); /* pointer to statically allocated task object memory */
        configASSERT(gBootTask != NULL);
    
        Bootloader_profileAddProfilePoint("FreeRtosTask Create");
    
        /* Start the scheduler to start the tasks executing. */
        vTaskStartScheduler();
    
        /* The following line should never be reached because vTaskStartScheduler()
        will only return if there was not enough FreeRTOS heap memory available to
        create the Idle and (if configured) Timer tasks.  Heap management, and
        techniques for trapping heap exhaustion, are described in the book text. */
        DebugP_assertNoLog(0);
    
        return 0;
    }
    


    The code is the main function of dss_display_share.c.

  • You can try declaring the symbol with extern keyword in ipc_rpmsg project. 

  • That is exactly how I did it. I defined a global variable in dss_display_share.c and used extern in ipc_rpmsg_echo.c, but the problem remains the same.
    I added DSS security monitoring code in dss_display_share.c. The plan is to use this global variable to send a message to Linux via IPC whenever a security interrupt happens. So, what should we do?

  • Please share a sample project replicating the issue.

  • hello,

    We add "uint8_t gSafetyCheckEnabled = 0U;" in dss_display_share.c,and add "extern uint8_t gSafetyCheckEnabled;"in ipc_rpmsg_echo.c in the ipc_rpmsg_echo_linux folder.  Then replace the ipc_recv_task_main  function. I can't insert the code, so I'll have to paste it instead.


    void ipc_recv_task_main(void *args)
    {
    int32_t status;
    char recvMsg[IPC_RPMESSAGE_MAX_MSG_SIZE+1]; /* +1 for NULL char in worst case */
    uint16_t recvMsgSize, remoteCoreId;
    uint32_t remoteCoreEndPt;
    //char *CallbackMsg = "Callback Message";
    #if defined DYNAMIC_ANALYSIS
    uint32_t cnt = 0;
    #endif

    /* New variables: Save Linux core connection info */
    static uint16_t linuxCoreId = 0;
    static uint32_t linuxEndPt = 0;
    static bool linuxConnected = false;
    static uint32_t activeMsgCounter = 0;
    static uint64_t lastActiveSendTime = 0;
    static uint32_t seqNumber = 0;

    RPMessage_Object *pRpmsgObj = (RPMessage_Object *)args;

    DebugP_log("[IPC RPMSG ECHO] Remote Core waiting for messages at end point %d ... !!!\r\n",
    RPMessage_getLocalEndPt(pRpmsgObj)
    );

    /* wait for messages forever in a loop */
    #if defined DYNAMIC_ANALYSIS
    while(cnt < DYNAMIC_ANALYSIS_MSG_COUNT)
    #else
    while(1)
    #endif
    {
    /* Modification 1: Use non-blocking or timeout receive to allow active message sending */
    recvMsgSize = IPC_RPMESSAGE_MAX_MSG_SIZE;

    /* Changed SystemP_WAIT_FOREVER to 500ms timeout to avoid permanent blocking */
    status = RPMessage_recv(pRpmsgObj,
    recvMsg, &recvMsgSize,
    &remoteCoreId, &remoteCoreEndPt,
    500); /* 500ms timeout */

    #if defined DYNAMIC_ANALYSIS
    cnt++;
    #endif

    /* If message received */
    if (status == SystemP_SUCCESS)
    {
    if (gbShutdown == 1u)
    {
    break;
    }

    if (gbSuspended == 1u)
    {
    continue;
    }

    /* Added: Record Linux core connection info */
    if (remoteCoreId == CSL_CORE_ID_A53SS0_0) { /* Linux core */
    if (!linuxConnected) {
    linuxCoreId = remoteCoreId;
    linuxEndPt = remoteCoreEndPt;
    linuxConnected = true;
    lastActiveSendTime = ClockP_getTimeUsec(); /* Set initial time */
    DebugP_log("[IPC RPMSG] Linux connected! Core ID: %d, EndPt: %d\r\n",
    linuxCoreId, linuxEndPt);
    }

    /* Print received message (for debugging) */
    recvMsg[recvMsgSize] = 0; /* add a NULL char at the end of message */
    DebugP_log("[IPC RPMSG] Received from Linux: %s\r\n", recvMsg);
    }

    /* echo the same message string as reply */
    /* Note: Keep as is here. When not echoing, printing can be skipped to reduce latency */
    #if 0
    recvMsg[recvMsgSize] = 0;
    DebugP_log("%s\r\n", recvMsg);
    #endif

    /* send ack to sender CPU at the sender end point */

    status = RPMessage_send(
    recvMsg, recvMsgSize,
    remoteCoreId, remoteCoreEndPt,
    RPMessage_getLocalEndPt(pRpmsgObj),
    SystemP_WAIT_FOREVER);
    DebugP_assert(status==SystemP_SUCCESS);

    if (gSafetyCheckEnabled)
    {

    status = RPMessage_send(
    CallbackMsg, strlen(CallbackMsg)+1,
    remoteCoreId, remoteCoreEndPt,
    RPMessage_getLocalEndPt(pRpmsgObj),
    SystemP_WAIT_FOREVER);
    DebugP_assert(status==SystemP_SUCCESS);

    }

    /* Increment counter */
    activeMsgCounter++;
    }
    /* If timeout (no message received), this is normal, continue with active send logic */
    else if (status == SystemP_TIMEOUT)
    {
    /* Timeout handling: Do nothing, continue with active send check below */
    }
    /* Other errors */
    else
    {
    DebugP_logError("[IPC RPMSG] RPMessage_recv error: %d\r\n", status);
    if (gbShutdown == 1u)
    {
    break;
    }
    }

    /* Modification 2: Regardless of message receipt, check if active sending is needed */
    if (linuxConnected) {
    uint64_t currentTime = ClockP_getTimeUsec();
    uint64_t elapsedTime = currentTime - lastActiveSendTime;

    /* Send message to Linux actively every 2 seconds */
    if (elapsedTime > 2000000) { /* 2 seconds = 2,000,000 microseconds */
    char activeMsg[IPC_RPMESSAGE_MAX_MSG_SIZE];
    uint16_t msgSize;

    /* Construct active message to send */
    snprintf(activeMsg, IPC_RPMESSAGE_MAX_MSG_SIZE-1,
    "ACTIVE_MSG[%u] from RTOS Core %d - Status: Running,",
    seqNumber++,
    IpcNotify_getSelfCoreId(),
    );
    activeMsg[IPC_RPMESSAGE_MAX_MSG_SIZE-1] = 0;
    msgSize = strlen(activeMsg) + 1; /* Include terminator */

    /* Actively send message to Linux */
    status = RPMessage_send(
    activeMsg, msgSize,
    linuxCoreId, linuxEndPt,
    RPMessage_getLocalEndPt(pRpmsgObj),
    SystemP_WAIT_FOREVER);

    if (status == SystemP_SUCCESS) {
    DebugP_log("[IPC RPMSG] Active message %u sent to Linux\r\n", seqNumber);
    lastActiveSendTime = currentTime;
    } else {
    DebugP_logError("[IPC RPMSG] Failed to send active message %u to Linux: %d\r\n",
    seqNumber, status);
    }
    }
    }

    /* Added: Print status periodically (optional) */
    static uint64_t lastStatusPrint = 0;
    uint64_t currentTime = ClockP_getTimeUsec();
    if (currentTime - lastStatusPrint > 5000000) { /* Print every 5 seconds */
    DebugP_log("[IPC RPMSG] Status: Linux connected=%s, TotalRx=%u, Seq=%u\r\n",
    linuxConnected ? "YES" : "NO", activeMsgCounter, seqNumber);
    lastStatusPrint = currentTime;
    }
    }

    gRecvTaskExitCounter++;
    if (gRecvTaskExitCounter >= IPC_RPMESSAGE_NUM_RECV_TASKS)
    {
    /* Follow the sequence for graceful shutdown for the last recv task */
    DebugP_log("[IPC RPMSG ECHO] Closing all drivers and going to WFI ... !!!\r\n");

    /* Close the drivers */
    Drivers_close();

    /* deinit system */
    System_deinit();

    if (gbShutdownRemotecoreID)
    {
    /* ACK the shutdown message */
    IpcNotify_sendMsg(gbShutdownRemotecoreID, IPC_NOTIFY_CLIENT_ID_RP_MBOX, IPC_NOTIFY_RP_MBOX_SHUTDOWN_ACK, 1u);
    }
    #if (__ARM_ARCH_PROFILE == 'R') || (__ARM_ARCH_PROFILE == 'M')
    /* For ARM R and M cores*/
    __asm__ __volatile__ ("wfi" "\n\t": : : "memory");
    #endif
    #if defined(BUILD_C7X)
    asm(" IDLE");
    #endif
    }
    vTaskDelete(NULL);
    }

  • Please confirm for which core the DSS file is build and for which core the IPC file is build?

    If it part of two different independent project or two different cores sharing a global variable from one c file to another is not possible. You can pass the value of variable from one core to another using IPC RPMSG Echo.

    Please refer DRIVERS_IPC_RPMESSAGE