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.

AM263P4-Q1: Get runtime in microsec order

Part Number: AM263P4-Q1

Tool/software:

Hi Team,

My customer is using TaskP_loadGet() to acquire the TaskP_Load struct, but they want to use the runTime() within the struct to get task run time in the usec order. Is it possible to do this?

TaskP_LOAD_UPDATE_WINDOW_MSEC is only able to get runtime upto 500msec. Is it only possible to measure runtime in 500msec order? 

Also, in the API for TaskP_loadGet(), it says "obj [out] Created object" but should it not be "obj [in] Created object"?

Best regards,

Mari Tsunoda

  • Okay, let's break down these questions about TaskP_loadGet and task run time.

    1. Can TaskP_Load.runTime provide task run time in microseconds?

    Yes, the runTime field within the TaskP_Load struct represents the accumulated run time of the task in microseconds.

    • Looking at the TaskP_Load struct definition:

    typedef struct {
    
        const char  *name;     /**< Name of the task */
        uint64_t runTime;   /**< Amount of time the task has run, units of usec */
        uint64_t totalTime; /**< Amount of time that has elapsed so far, units of usec */
        uint32_t cpuLoad;   /**< CPU load in units of percentage with 2 decimal point precision, i.e 1234 means 12.34% */
    
    } TaskP_Load;

    The comment explicitly states the unit is microseconds (`usec`).

    • The calculation within TaskP_loadGet populates this field using taskObj->accRunTime:

        taskLoad->runTime = taskObj->accRunTime;

    • taskObj->accRunTime is updated in TaskP_updateLoad by calculating the difference (delta) between consecutive readings of the FreeRTOS run time counter for the task.

    static void TaskP_updateLoad(TaskP_Struct *taskObj)
    {
    #ifndef SMP_FREERTOS
    #if (configGENERATE_RUN_TIME_STATS == 1)
        uint32_t runTimeCounter = ulTaskGetRunTimeCounter(taskObj->taskHndl);
    
        uint32_t delta = TaskP_calcCounterDiff(runTimeCounter, taskObj->lastRunTime);
    
        taskObj->accRunTime += delta;
        taskObj->lastRunTime = runTimeCounter;
    #endif
    #else
        uint32_t delta;
        TaskStatus_t taskStatus;
    
        vTaskGetInfo(taskObj->taskHndl, &taskStatus, pdFALSE, eReady);
    
        delta = TaskP_calcCounterDiff(taskStatus.ulRunTimeCounter, taskObj->lastRunTime);
    
        taskObj->accRunTime += delta;
        taskObj->lastRunTime = taskStatus.ulRunTimeCounter;
    #endif
    }

    • The FreeRTOS run time counter itself (ulTaskGetRunTimeCounter or taskStatus.ulRunTimeCounter) relies on portGET_RUN_TIME_COUNTER_VALUE(), which is implemented via uiPortGetRunTimeCounterValue(). This function, in the provided ports, uses ClockP_getTimeUsec():

    uint32_t uiPortGetRunTimeCounterValue()
    {
        uint64_t timeInUsecs = ClockP_getTimeUsec();
    
        /* note, there is no overflow protection for this 32b value in FreeRTOS
         *
         * This value will overflow in
         * ((0xFFFFFFFF)/(1000000*60)) minutes ~ 71 minutes
         *
         * We call vApplicationLoadHook() in idle loop to accumlate the task load into a 64b value.
         * The implementation of vApplicationLoadHook() is in source\kernel\freertos\dpl\common\TaskP_freertos.c
         */
        return (uint32_t)(timeInUsecs);
    }

    (Similar implementations exist in other port.c files like source/kernel/freertos/portable/TI_ARM_CLANG/ARM_CR5F/port.c)

    • ClockP_getTimeUsec() is designed to return the time in microseconds.

    Therefore, the runTime field accumulates the task's execution time measured in microseconds.

    2. Is the runtime measurement limited by TaskP_LOAD_UPDATE_WINDOW_MSEC (500ms)?

    No, the measurement itself is not limited to a 500ms granularity or duration.

    • TaskP_LOAD_UPDATE_WINDOW_MSEC defines the interval at which the TaskP_loadUpdateAll function is typically called from the vApplicationLoadHook (which usually runs within the FreeRTOS Idle task).

    void vApplicationLoadHook(void)
    {
        static uint64_t lastUpdateTime = 0;
        uint64_t curUpdateTime = ClockP_getTimeUsec();
    
        if( (curUpdateTime > lastUpdateTime) && ((curUpdateTime - lastUpdateTime) > (TaskP_LOAD_UPDATE_WINDOW_MSEC*1000u )) )
        {
            TaskP_loadUpdateAll();
            lastUpdateTime = curUpdateTime;
        }
    }
     * \brief The update rate at which TaskP_loadUpdateAll() is called
     */
    #define TaskP_LOAD_UPDATE_WINDOW_MSEC   (500u)


    • TaskP_loadUpdateAll updates the internal accumulated run time (taskObj->accRunTime) for all registered tasks based on the underlying microsecond timer.

    void TaskP_loadUpdateAll(void)
    {
        TaskP_Struct *taskObj;
        uint32_t i;
    
        vTaskSuspendAll();
    
        for(i=0; i<TaskP_REGISTRY_MAX_ENTRIES; i++)
        {
            taskObj = gTaskP_ctrl.taskRegistry[i];
    
            if(taskObj != NULL)
            {
                TaskP_updateLoad(taskObj);
            }
        }
    
        TaskP_updateIdleTaskLoad();
        TaskP_updateTotalRunTime();
        
        (void)xTaskResumeAll();
    }

    • When you call TaskP_loadGet, it reads the current value of this accRunTime (potentially forcing an update first if getting the load of the current task, or relying on the last update from the hook).

    void TaskP_loadGet(TaskP_Object *obj, TaskP_Load *taskLoad)
    {
        TaskP_Struct *taskObj = (TaskP_Struct *)obj;
    
        if(taskObj->taskHndl == xTaskGetCurrentTaskHandle())
        {
            /* Perform a force yield for the kernel to update the current task run time counter value. */
            taskYIELD();
        }
    
        vTaskSuspendAll();
    
        TaskP_updateLoad(taskObj);
        TaskP_updateTotalRunTime();
    
        taskLoad->runTime = taskObj->accRunTime;
        taskLoad->totalTime  = gTaskP_ctrl.accTotalTime;
        taskLoad->cpuLoad = TaskP_calcCpuLoad(taskObj->accRunTime, gTaskP_ctrl.accTotalTime);
        taskLoad->name = pcTaskGetName(taskObj->taskHndl);
    
        (void)xTaskResumeAll();
    }

    • The runTime field in TaskP_Load represents the total accumulated microseconds the task has run since the load statistics were last reset using TaskP_loadResetAll. It's not just the time run in the last 500ms window. The 500ms window only affects how frequently the accumulated value gets refreshed in the background by the hook. The underlying timer resolution is still microseconds.

    3. Is the documentation for TaskP_loadGet parameter obj correct ([out] vs [in])?
    You are correct. The documentation comment for the obj parameter in TaskP_loadGet appears to be incorrect in TaskP.h.
  • Hi Akshit,

    Thanks for the detailed response.

    My customer has a follow-up question. 

    Is the timing precision for the TaskP_loadGet() function and ClockP_getTimeUsec() the same? Would they get the same results to the same degree of accuracy? They are looking for an alternative to TaskP_loadGet() because the results will reflect the CPU load from TaskP_loadResetAll() as well, but they want to pure task time.

    Best regards,

    Mari

  • The FreeRTOS run time counter itself (ulTaskGetRunTimeCounter or taskStatus.ulRunTimeCounter) relies on portGET_RUN_TIME_COUNTER_VALUE(), which is implemented via uiPortGetRunTimeCounterValue(). This function, in the provided ports, uses ClockP_getTimeUsec():

    Hi Mari,

    As mentioned here, the FreeRTOS run time counter uses ClockP_getTimeUsec() under the hood.

    So yes, the timing precision should be same!

    Regards,
    Akshit