PIC32 port: System service timer not running properly when it's ISR is deferred

Hi Richard @rtel sir, i hope you are fine during this corona pandemic.
I am using PIC32MZ2048EFH144 micro-controller custom board. I am using a system timer which runs at 1 msec and systiccount = 1000 ticks/sec. But when i defer the ISR to a separate task, the timer is running at 1 msec but systiccount = 20 ticks/sec. I don’t know the reason for this unexpected slow down.
In application, i am using the timer to send data to server for every 20 sec. This slow running of timer adversely affecting the application.
Here i am providing the details specific to application:
1. In general the system service timer is as below:

void __ISR(_TIMER_2_VECTOR, ipl1AUTO) IntHandlerDrvTmrInstance0(void)
{
DRV_TMR_Tasks(sysObj.drvTmr0); // The API which is to be deferred and provides **
** systiccount

}

  • But as per Freertos rule, non RTOS api shoud not be called in ISR so i decided to defer the ISR
  1. I deferred the Timer ISR with a task delay of 100 msec and given the highest priority.
    But after deferring the timer ISR runs at 1 msec/sec but Systiccount provided by the "DRV_TMR_Tasks(sysObj.drvTmr0) " API is 20 Ticks/sec.
  2. Without ISR deferring , ISR runs at 1 msec and Systiccount is 1000 tics/sec which is same as ISR.
  3. i guess task delay might be affecting the timer and scheduler tick timer runs at 4.048 msec. Also i tried to run the tick timer at 1 msec but no change in result.

So, what might be the reason for this huge reduced timer count? Does context switching causing the error?

I’m not fully following your scenario.

Is this the timer you are using to generate the RTOS tick interrupt, or a separate free running timer that increments ever 1ms?

Does this mean the RTOS tick frequency is 1KHz?

So this is the interrupt handler for the timer, so it does not look like the RTOS tick timer, but a separate timer - perhaps answering my own first question.

Can you show the code you use to defer this to the task. I assume sysObj.drvTmr0 is just returning the timer’s count value - is that right?

I don’t understand this part - where is the delay? How are you creating a 100ms delay?

DRV_TMR_Tasks(sysObj.drvTmr0) is a Microchip specific API within their framework.
I have avoided using the Harmony framework for its poor support of FreeRTOS so can’t help with that aspect.

The PIC32 port has a timer tick ISR defined in port.c and port_asm.S this ISR calls the xTaskIncrementTick() kernel function which, if enabled by setting configUSE_TICK_HOOK in the configuration file, calls vApplicationTickHook() residing in freertos_hooks.c

This is where you would notify a task, using a FromISR version of the kernel API, that there are jobs to be done on a periodic basis.
Alternatively, use an auto-reload software timer with a call back to achieve the same.

@rtel sir, here is the code snippet:

In system_tasks.c file:

static void _SYS_TMR_Tasks(void)
{
while(1)
{
xSemaphoreTake(xSysTMR_ISR,pdMS_TO_TICKS(10));
DRV_TMR_Tasks(sysObj.drvTmr0); // which is deferred from ISR and gives
Systiccount

vTaskDelay(pdMS_TO_TICKS(100));
}
}

In System_interrupt.c file:

void __ISR(_TIMER_2_VECTOR, ipl1AUTO) IntHandlerDrvTmrInstance0(void)
{
testcountTimer2++;
IFS0bits.T2IF = 0;

  BaseType_t xHigherPriorityTaskWoken;
  xHigherPriorityTaskWoken = pdFALSE;

  if(xSemaphoreGiveFromISR(xSysTMR_ISR,&xHigherPriorityTaskWoken) == pdTRUE){
   portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
   }    

}

For your clarification to these questions:
1.Is this the timer you are using to generate the RTOS tick interrupt, or a separate free running timer that increments ever 1ms?
I am using TIMER_1 for RTOS Tick frequency and TIMER_2 for System service timer.

  1. Can you show the code you use to defer this to the task. I assume sysObj.drvTmr0 is just returning the timer’s count value - is that right?
    yes , sysObj.drvTmr0 calls a callback function which provides count value. The following is the code:

In Sys_tmr.c file:
Here is the DRV_TMR_Tasks(sysObj.drvTmr0) API’s definition:
a)
void DRV_TMR_Tasks ( SYS_MODULE_OBJ object )
{
DRV_TMR_MODULE_DESCRIPTOR* pDcpt = _DRV_TMR_ModuleObj(object, true);

if(pDcpt)
{
    DRV_TMR_ProcessEvents(pDcpt);      //  (b)
}

}

b) DRV_TMR_ProcessEvents(pDcpt) API In which Alarm callback API will be called

static void DRV_TMR_ProcessEvents ( DRV_TMR_MODULE_DESCRIPTOR* pDcpt )
{
DRV_TMR_MODULE_INSTANCE* pTmrInst = &pDcpt->tmrInstance;

/* Check if the Timer Interrupt/Status is set */

// if ( SYS_INT_SourceStatusGet ( pTmrInst->interruptSource ) != false )
// {
/* Clear Timer Interrupt/Status Flag /
SYS_INT_SourceStatusClear ( pTmrInst->interruptSource );
// process clients
int ix;
DRV_TMR_CLIENT_OBJ
pClient = pDcpt->tmrClients;
for(ix = 0; ix < sizeof(pDcpt->tmrClients) / sizeof(pDcpt->tmrClients); ix++, pClient++)
{
if(pClient->clientStatus == DRV_TMR_CLIENT_OBJ_RUNNING)
{
/
increment the alarm */
pClient->alarmCount++;

            /* callback the application routine */
            if( pClient->alarmCallback != NULL )
            {
                (*pClient->alarmCallback)( pClient->context, pClient->alarmCount);  // (C) 
                                                                                                                                                     
            }

            /* Alarm in one shot mode */
            if ( pClient->alarmPeriodic != true )
            {
                _DRV_TMR_Suspend(pClient);     // Stop Timer
                _DRV_TMR_ClientClear(pClient); // Clear timer client
            }
        }
    }

// }
}

C)

static void _SYS_TMR_AlarmCallback ( uintptr_t context, uint32_t alarmCount )
{

SYS_TMR_OBJECT* pTmrObj = (SYS_TMR_OBJECT*)context;

if(alarmCount < pTmrObj->sysTickCount)
{   // overflow
    pTmrObj->sysTickCountHigh++;
}
// set new count
pTmrObj->sysTickCount = alarmCount;  // **Where i get sysTickCount  value**

#if (SYS_TMR_INTERRUPT_NOTIFICATION)
_SYS_TMR_ProcessIsrAlarm();
#else
pTmrObj->alarmReceived = true;
#endif

}

  1. I don’t understand this part - where is the delay? How are you creating a 100ms delay?
    This delay is the VTaskdelay(pdMS_TO_TICKS(100)).
    I think i have provided answers for your questions. hopefully i am expecting a solution as early as possible sir.
    Thank you!

Ofcourse @SergentSloGin you are right. But i must have to use the system Service timer since microchip made system service timer as default for its frame work. So that if timer doesn’t run well , all services will be affected badly.

Sorry, but I don’t understand what this means. Normally if you defer interrupt processing to a task you signal a task which wakes and performs any processing necessitated by the interrupt - or alternatively you send a function to a task to get executed there. This does not appear to fit either of those patterns. It looks more like a periodic task that executes every 10ms.

Sir @rtel Actually This is the ISR provides Systiccount value .
void __ISR(_TIMER_2_VECTOR, ipl1AUTO) IntHandlerDrvTmrInstance0(void)
{
DRV_TMR_Tasks(sysObj.drvTmr0); // The API which is to be deferred and provides **
** systiccount

}
But now i want to defer this " DRV_TMR_Tasks(sysObj.drvTmr0);" API into a separate task like as i shown in my previous post. Can’t i do that?
As per the documentation , a non-Rtos API should not be call in an ISR so that i want to defer that. But when i defer that API it is giving wrong count.
Shouldn’t i defer an API which calls a callback function? If i don’t defer it , i have chances of getting exception errors

This really confuses me. Are you saying that you are trying to defer the code which updates the system tick counter to inside a task?

In Harmony the timer interrupt sets a flag and then the function SYS_TMR_Tasks will be called from within the superloop function SYS_Tasks(). If you enable timer driver interrupts the callbacks will be called from within the hardware timer ISR.

I am trying to figure out what you mean by “deferred”, because if you mean that the timer callback should be called from the task instead of the ISR, then I cannot understand how this could possibly work.

When using hardware timers with interrupts the only way that the timers can happen at the wrong frequency is if the timer is not set up correctly. If you are saying things are taking longer than you expect it does not sound like you are doing this?

Yes, absolutely you are right!
i am agree with this statement: “If you enable timer driver interrupts the callbacks will be called from within the hardware timer ISR.”.
But as per FreeRTOS documentation, a Non-FreeRtos API shoud not be called from a ISR, then how can i call “callbacks” from hardware timer ISR. I must have to call it from inside a Task. This is what i mean. Hopefully you understand my doubt.
Sorry for troubling you all but i am facing application crash due to this.

Also please refer to this discussion which can explain my intention behind this topic:

see here also:

SergentSloGinJean Rubillon

Mar 12

@saikumar.k Exception code 6 is mainly caused by either calling a non ISR API function from an ISR or using an application/RTOS object before it has been created.
No one here knows your code so it’s impossible for us to tell you exactly what is wrong with it. Hopefully we’re pointing you in the right direction, as that is the only thing we can realistically do.

Sir @rtel , can you please have a look on the above post. It is very necessary to overcome this problem.

I’m afraid I am really not familiar with the structure of the libraries you are using and this may be a harmony question more than a FreeRTOS question. Some general notes:

I don’t think we have any documentation that says that. I think the rule you are thinking of is that no non ISR specific FreeRTOS API should be called from an interrupt. For example you cannot call xSemaphoreGive(), but can call xSemaphoreGiveFromISR(). FreeRTOS FAQ - FreeRTOS API - FreeRTOS

If you want to use an interrupt to trigger code executing in a task then I would recommend reading this page: FreeRTOS - Memory management options for the FreeRTOS small footprint, professional grade, real time kernel (scheduler)

1 Like

This is more a Microchip Harmony framework understanding issue than a FreeRTOS one. I would suggest you query Microchip’s forum and also read the Harmony framework documentation. I noticed that Harmony seems to support only the PIC32MZ family and all of the Atmel SAM variants.

Unfortunately, although I did try for a couple of days to get going with Harmony and FreeRTOS on a PIC32MX, I decided that Harmony was just not going to play ball for me and went with only FreeRTOS. I still used some of the code generated by Harmony using a separate dummy project to configure the peripherals in an easier way than reading the 1000+ page manual :wink:

1 Like

Moving this thread to the @Microchip category so our partner can possibly provide a solution. @shridhar.

2 Likes

support@microchip.com can you please review this issue

Dear all ,
Thanks for your concern regarding this issue. Hope i get a solution as early as possible.

This part I can help you with some advice.

Firstly I would be quite surprized if the FreeRTOS documentation said that non-FreeRTOS API’s cannot be called from the ISR?? Are you sure this is what you meant to say? I know the documentation explains that some FreeRTOS API’s are not safe to call from the ISR, and sometimes there are _FromISR variants which are safe to call, but I have never heard a claim that non-FreeRTOS API’s are subject to any rules like that. Can you please point out where you saw this in the documentation and perhaps we can clarify it?

When you are going to call a callback you need to most importantly decide from which context it should be called. If you are not going to call any FreeRTOS API functions inside the callback, or if you will stick to the _FromISR versions which are safe to call, then calling from the ISR is perfectly fine.

If you have to call non-safe API’s you need to delegate the call to a task of your choice. There are many mechanisms to do this. A common pattern is to create a task (or sometimes tasks) which is dedicated to make these callbacks. Usually you can let this task sleep until an event happens. There are various ways that you could notify the task to wake and call the callback - my favorite way is to use direct to task notifications (see FreeRTOS task notifications, fast Real Time Operating System (RTOS) event mechanism), but you could also use queues or semaphores to accomplish this less efficiently.

1 Like

Sorry for the wrong statement. I mean to say non-safe API but i said non-RTOS. Any how thanks for the description. Also sorry for the delayed response since i was involved with ventilators project.