TI C2000 Series: Runtime statistics for C28x CPU

Hi Experts,

This is regarding microcontroller: TMS320F28388D

I have implemented to gather the Runtime statistics in 33 ms TASK of FreeRTOS running on c28x cpu.

The issue: Idle task returns to give approx 96% of CPU load. That means CPU load is only at approx. 4%. But based on my calculation of CPU load contributed by ISRs, they alone contribute to 40% of CPU load. So, where is the issue?

Before jumping on this, let me clarify the way I calculated CPU load contributed by ISRs.

On C28x CPU:
6 Tasks run:

  1. Four are periodic tasks: “5ms task”, “20 ms task”, “33 ms task”, “500 ms task”
  2. Two are non-periodic tasks that are on blocking wait on semaphore given by ISR: “Zero Cross Task1” and “Zero Cross Task2”.

4 ISRs:

  1. ISR1 @ 70 KHz - Triggers ISR2 at every second pass
  2. ISR2 @ 35 KHz - Triggers ISR3 at every eighth pass
  3. ISR3 @ 4.35 KHz

In each ISR, at entry and exit points, I am using a counter to obtain start tick, stop tick and delta between them.

The timer configurations are:

  1. Free running
  2. Down counter
  3. clock pre-scaling = 1
  4. Period at 100000 where sys clock is 200 MHz

That means the CPU timer tick is 100000/100000000 = 1 ms

So, the code in the ISR looks like below:

__interrupt void isr1(void)
{
        //Store timer ticks at start of ISR for calculating ISR execution time
        isrExecutionTicks.invIsr.isrStartTicks = CPUTimer_getTimerCount(CPUTIMER2_BASE);
        isr_count++;

        /*code in ISR*/

        isrExecutionTicks.invIsr.isrStopTicks = CPUTimer_getTimerCount(CPUTIMER2_BASE);
        if(isrExecutionTicks.invIsr.isrStartTicks < isrExecutionTicks.invIsr.isrStopTicks) //overflow condition
        {
            //stores execution time in microseconds
            Cpu1.IsrExecutionTime.invIsrExecutionParameters.deltaTime += (float)(( isrExecutionTicks.invIsr.isrStartTicks + (configCPU_CLOCK_HZ / configTICK_RATE_HZ) - isrExecutionTicks.invIsr.isrStopTicks ) * ( CONVERT_SEC_TO_US / (float)DEVICE_SYSCLK_FREQ));
        }
        else
        {
            //stores execution time in microseconds
            Cpu1.IsrExecutionTime.invIsrExecutionParameters.deltaTime += (float)((isrExecutionTicks.invIsr.isrStartTicks - isrExecutionTicks.invIsr.isrStopTicks) * ( CONVERT_SEC_TO_US / (float)DEVICE_SYSCLK_FREQ));
        }
}

In the 33 ms task, the code looks like below:

Cpu1.IsrExecutionTime.invIsrExecutionParameters.executionPercent = (Cpu1.IsrExecutionTime.invIsrExecutionParameters.deltaTime/TIME_SLICE)*100; //TIME_SLICE = 33000

In this way, sum of executionPercent of all ISRs are added and that brings approx. 40% CPU usage.

Now, coming back to FreeRTOS Runtime statistics, below are the configurations:
File: FreeRTOSConfig.h

// Run time statistics related configurations
#define configCPU_CLOCK_HZ                        200000000
#define configTICK_RATE_HZ                        1000

#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY      1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() configureTimeForRunTimeStats()
#define portGET_RUN_TIME_COUNTER_VALUE()   getRunTimeCounterValue()

Definition of Timer configuration, which is being used by RUM_TIME_STATS:

void configureTimeForRunTimeStats()
{
    CPUTimer_setEmulationMode(CPUTIMER1_BASE, CPUTIMER_EMULATIONMODE_RUNFREE);
    CPUTimer_setPreScaler(CPUTIMER1_BASE, 0U); // no prescaling
    CPUTimer_setPeriod(CPUTIMER1_BASE, 0xFFFFFFFE); //largest possible value
    CPUTimer_disableInterrupt(CPUTIMER1_BASE);
    CPUTimer_stopTimer(CPUTIMER1_BASE);

    CPUTimer_reloadTimerCounter(CPUTIMER1_BASE); //reload the period
    CPUTimer_startTimer(CPUTIMER1_BASE);
}

uint32_t getRunTimeCounterValue()
{
    return (0xFFFFFFFE - CPUTimer_getTimerCount(CPUTIMER1_BASE));
}

And from 33 ms task, I am using below code to get the CPU load obtained using IDLE task contribution:

void calculateCPUUsageforTSKs(stCpu1IsrExecutionTime* executionParameters)
{
   static uint32_t ulLastTotalRunTime = 0;
   static uint32_t ulLastIdleTime = 0;
   TaskStatus_t pxTaskStatusArray[7];
   uint32_t ulTotalRunTime = 0;
   volatile UBaseType_t uxArraySize, x;
   uint32_t ulIdleTime = 0;
   // Get number of tasks
   uxArraySize = uxTaskGetNumberOfTasks();
   // Allocate array for task status
   //pxTaskStatusArray = (TaskStatus_t *)pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
   if(pxTaskStatusArray != NULL)
   {
       // Get task information
       uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime);
       // Find idle task runtime
       // Calculate CPU usage since last measurement
       for(x = 0; x < uxArraySize; x++)
       {
           if(strstr(pxTaskStatusArray[x].pcTaskName, "IDLE") != NULL)
           {
               ulIdleTime = pxTaskStatusArray[x].ulRunTimeCounter;

			   break;
           }
       }
	   if(ulLastTotalRunTime != 0)
	   {
		   uint32_t ulDeltaTotalTime = ulTotalRunTime - ulLastTotalRunTime;
		   uint32_t ulDeltaIdleTime = ulIdleTime - ulLastIdleTime;

		   if(ulDeltaTotalTime > 0)
		   {
			   executionParameters->idleTSKExecutionParameters.executionPercent = ((float)ulDeltaIdleTime * 100.0 / (float)ulDeltaTotalTime);
		   }
	   }

       // Update last values
       ulLastTotalRunTime = ulTotalRunTime;
       ulLastIdleTime = ulIdleTime;
   }
}

The result from above function is subtracted from 100 to get the total CPU Load% and this is returning 4% of CPU Load.

I have used the modified version of above function to obtain the contribution of other 6 tasks but added together, all of them are contributing to 3~4% of CPU load.

In principle, (100 - IDLE task load%) should return CPU load contributed by everything, including ISRs.
So, why is IDLE task reporting 96% usage? where is the mistake I am doing in calculation? Is it in ISR calculation or in runtime statistics timer configuration?

Please help.

Thanks,
Rohit

NO. The base FreeRTOS task loading charges ISR usage to whatever task was interrupted, and is not tallied separately. If you want to tally ISR usage separately, you will need to modify your ISRs and the Run Time stats code to do that.

This isn’t trivial, but not overly complicated either. I have code to do this that I have written for the company I work for, but that code is considered proprietary. It requires uses the freertos_tasks_c_additions.h hook to get access to the needed information in the TCBs, and has every ISR (or every ISR who’s time you want to keep seperate) begin and end with a call to a routine to handle keeping track of the timing.

Hi Richard,

Thanks for your reply and making it clear about IDLE task.
Could you please share an example code, if sharing original code is not possible?

Hard to reduce a basic routine to just an example, but here is a description.

The code is put into a file name freertos_tasks_c_additions.h, and FreeRTOSConfig.h has the line

#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1

in it to make FreeRTOS’s task.c include that file into it.

The file declares a global variable to keep track of what the “current ISR” is, and is initialzed to say that it isn’t in an ISR initially, as well as an array of elements of type configRUN_TIME_COUNTER_TYPE to accumulate the ISR usage, with an entry for each ISR.

Every ISR begins with a line like:

int state = isrStart(isrNumber);

where isrNumber is the number assigned to that ISR, to us as an index into that array, and ends with a

isrEnd(state);

isrStart saves the current value of the current ISR (which it will return to use for the later call to isrEnd) then get the current run time counter into ulTotalRunTime (or its SMP equivalent) and increment the usage of what was previously running (the isr or task that we interurpted) by the difference between ulTotalRunTime and ulTaskSwitchedInTime, and then set ulTaskSwitchedInTime to ulTotalRunTIme, update the current ISR value and return the previous value.

isrEnd does similar work but updates the current ISR’s run time and then sets the current ISR based on the parameter it was given.

So, this seems to be same like implementing a free running counter and calculate the time spent in each ISR, and then calculate to cpu load from a periodic task.

This is something that I have explained in my question and have implemented in all ISRs, using a free running counter.

You want to use the SAME free running counter as the task stats, and update the variables it uses to calculate the used time, so it doesn’t count the ISR times in the tasks.

That is why it needs to be injected into task.c via the freertos_tasks_c_additions.h hook, so you can access those variables.

Got it, but I am having difficulty to follow your suggestion. Forgive me as I am new in FreeRTOS. I have translated my understanding into the code below. Please correct it wherever needed.

In FreeRTOSConfig.h

#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1
volatile uint32_t currentISR = 0; //initialized to no ISR type

enum isr_list{
        ISR1 = 1,
        ISR2,
        ISR3
};

configRUN_TIME_COUNTER_TYPE isrList[3];
//code for enabling and configuring run time stats follows

Definitions of isrStart and isrEnd.

uint32_t isrStart(uint32_t isrNumber)
{
         uint32_t thisISR = isrNumber; //is this what you meant?
         ulTotalRunTime = getRunTimeCounterValue();
         isrList[currentISR] += (ulTotalRunTime - ulTaskSwitchedInTime); //previous ISR run time updated
         ulTaskSwitchedInTime = ulTotalRunTIme;
         currentISR = isrNumber;
         return thisISR;
}

void isrEnd(uint32_t state)
{
         isrList[state] = += (ulTotalRunTime - ulTaskSwitchedInTime); //update current ISR run time
         currentISR = state; //set the current ISR
}

the return value for isrStart should be what currentISR was when isrStart was called.

that value might indicate that you were not in an ISR, and thus that time needs to be added to the task pointed to by pxCurrentTCB instead of an isrList entry.

isrEnd needs to update the info for currentISR (which here we know will be an ISR) before resetting it back to the value from isrStart telling us what ISR (or if it was a task) that was interrupted.

You also want to think about what needs to be done to make these operation atomic so a second interrupt occuring during this code doesn’t cause problems.

Other comments looking at the code.

currentISR should not be defined in a header, as that means that every file that includes that header will define that variable, giving multiple definitions.

ISR numbers being 1, 2, 3, should not be used to index an array isrList[3], as that only has subscripts 0, 1, 2. either index with currentISR-1, or number 0, 1, 2 and use -1 as no ISR (and thus should be an int, not unsigned)