Can't xSemaphoreGiveFromISR() during HW timer - polling task faster than FreeRTOS tick allows

Good day

I am using a hardware timer to execute a task at a faster rate than what the FreeRTOS ticks will allow (1kHz limit).
Currently, I am using a 100us period (10kHz rate).
In this timer’s interrupt handler, I call xSemaphoreGiveFromISR(taskSemphr, higherPrioTskWkn), but this doesn’t work, as the task continues to wait on
if(xSemaphoreTake(taskSemphr, portMAX_DELAY) == pdTRUE).

The HW timer’s interrupt priority is set to (configMAX_SYSCALL_INTERRUPT_PRIORITY-1).
I also have vApplicationTickHook() enabled.
Perhaps there is an interaction? Or perhaps I am polling this task incorrectly?

Thanks in advance.

First of all, the taskHandle is probably misnamed, right? It should be a semaphore handle which of course you can name whatever you like, but just to make sure you don’t mistakingly pass a task handle here?

Could it be that your ISR fires so fast that no task has a chance to execute?

Hi @RAc
Yes, you’re right about the taskHandle being renamed - I made a typo in my question.
I’ll edit my post now (it is a semaphore in my code).

Yes, I guess it is possible that this task hasn’t finished executing by the time the semaphore is given again.
However, other tasks are definitely executing.

How do you create your semaphore?

@RAc
image

well it’s kind of tedious to only get pseudo code fragments from you in little chunks. For example, it’s obvious that

xSemaphoreGiveFromISR(taskSemphr, higherPrioTskWkn)

won’t work as written (unless higherPrioTskWkn is a pointer variable; normally an address operator on a variable is used). Can you post some real code from your code base?

@RAc

Once again, my apologies for my mistake.
I agree, it makes things tricky when you get bits and bobs of incorrect information.

In the mean time, we have found the problems.
Firstly, the stack size of the task wasn’t big enough.
Secondly, it looks like the task requires some kind of ‘boot-up delay’ when it executes for the first time.
We believe that some of the variables used by the task are not initialised in time for the first execution of this task.

So, the task’s structure looks like this now:

void myTask(void *param)
{
    //init local vars

    vTaskDelay(100); // delay of 100 ticks (in my case, ms) for init of task & vars

    while(1)
    {
        if(xSemaphoreTake(taskSemphr, portMAX_DELAY) == pdTRUE)
        {
            //do task's stuff
        }
    }
}

ok, thanks for the wrap up, great you got it working!

One piece of advice though: The delay you put in before the while() loop is (according to the comment behind it) one of the top ten trap doors in concurrent programming. DO find out WHY you need a delay and DO NOT use an absolute delay; instead, synchronize the task execution with whatever is needed to set up everything correctly. If you just put in some random delay that appears to work without knowing the inner works, Murphy will mandate that the delay is too short in some random installation out there in the field. Or you may have barked up the wrong tree and the delay is just something that looks like it solved your problem whereas in reality it just deferrs it.

1 Like

You’re right, it could be a nasty problem at a later point!
Thanks, you’ve been super helpful :smiley:

The following article might be of interest: https://freertos.org/2020/09/decrease-ram-footprint-and-accelerate-execution-with-freertos-notifications.html

1 Like

Wonder if your issue is another task creates the semaphore?

Fundamental principle, don’t use something you are not SURE is initialized yet.

Best solution is make the semaphore before you start FreeRTOS. (Think what might happen if the ISR got called before the semaphore was setup.)

1 Like

Thanks @richard-damon

I create the semaphore before I call vTaskStartScheduler(), so that isn’t my problem.
I’ll update this forum if I find the problem!

Hi @richard-damon and @RAc

I did a bit more digging into the task’s ‘boot-up delay’ I mentioned.
First, I removed the delays from the relevant tasks and then I started a debug session.

The system doesn’t run past a certain point (when it enters a task that used to have a delay for the first time, I believe).
Here is the full call stack:
image

The debugger always stops me here in port.c (I started and stopped the processor a few times):

If I step one level up, I see my task:

Another level up:

Another level up shows that it is waiting on taskEXIT_CRITICAL():
image

Another level up shows it hasn’t exited vPortExitCritical():
image

The second last reference has no source code to refer to:
image

And then the final level on the call stack is a completely unrelated General Purpose Timer interrupt handler (used for FreeRTOS Runtime stats):
image

Update: I’ve paused it a few more times, and it always lands at the same place in pxPortInitialiseStack(). Sometimes the functions ‘higher’ in the call stack are different, but GPT2_IRQHandler() is always the highest function on the call stack, so this is probably where the issue lies?

Here is how the GPT2_IRQHandler() is set up:

And here’s what the related portion in FreeRTOSConfig.h looks like:
image

Please let me know if you have suggestions for what I should do next.

Kind regards

I think you are going in the wrong direction in the stack trace. The GPT2_IRQHandler is the last item on the stack, so it seems that you are stuck in that ISR. Perhaps there is an interrupt flag that needs to be cleared so you can exit the ISR, or it is generating interrupts faster than you can process them.

At the end of the interrupt handler are you calling:

portYIELD_FROM_ISR( higherPrioTskWkn );

If you don’t call this the task won’t wake until the next tick.

Hi @RealtimeRik

Thank you for your reply!
Are you referring to GPT2_IRQHandler()?

If so, I know that I haven’t included a call to portYIELD_FROM_ISR(), but I thought that was only necessary if we call a FreeRTOS function to start another task?
Because there must be something to set the value of higherPrioTskWkn?

If you are referring to the interrupt handler I spoke of earlier, I am indeed calling portYIELD_FROM_ISR(xHigherPrioTskWkn);, right after giving the semaphore.

Kind regards

Hi @richard-damon

Thank you for your response.
I believe that the interrupt flags are cleared, and that the Cortex-M7 I am using should handle the rate of these GPT interrupts (100us period).

I have two ideas as to what it might be:

  1. The GPT2 interrupt occurred at the same time as the FreeRTOS tick handler
  2. Two interrupts of the same priority occurred at approximately the same time, causing the second interrupt to disrupt the first interrupt’s IRQ handler.

Could you perhaps give your opinion on whether any of them could give this behaviour?

I have two more questions:

  1. What interrupt priority does the FreeRTOS tick handler have?
  2. What happens if a FreeRTOS tick occurs while we are busy with another IRQ handler?

Kind regards

I was referring to the handler you spoke of earlier. Sounds like you are doing it right.

1 Like

Usually the lowest prio. See configKERNEL_INTERRUPT_PRIORITY in FreeRTOSConfig.h

It’s delayed/kept pending given it has the lowest interrupt prio.

You could/should set your non-FreeRTOS GPT2 interrupt to a (logically) prio >= configMAX_SYSCALL_INTERRUPT_PRIORITY +1 if you want to avoid any interference with the FreeRTOS part (interrupts/critical sections) of your application.
Then and with nested interrupts enabled (default) your GPT2 interrupt will interrupt any other running ISR of an interrupt with a logically lower prio.

1 Like

Thanks @hs2!

In this case, I don’t want my GPT2_IRQHandler() to interrupt any of the other ISRs, so I’ll give it a lower priority than the others.