Reasons that as task "wakes" early from a vTaskDelay(x)?

I’m using a trial of Tracealyzer and see something pretty concerning.

I’m using the AWS FreeRTOS wifi reference implementation, FreeRTOS v10.4.x. Here is the config.h:

#define configTICK_SOURCE                                                         FREERTOS_USE_RTC

#define configUSE_PREEMPTION                                                      1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION                                   1
#define configUSE_TICKLESS_IDLE                                                   0
#define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG                                      1 /* See into vPortSuppressTicksAndSleep source code for explanation */
#define configCPU_CLOCK_HZ                                                        ( SystemCoreClock )
#define configTICK_RATE_HZ                                                        1000
#define configMAX_PRIORITIES                                                      ( 15 )
#define configMINIMAL_STACK_SIZE                                                  ( 600 )
#define configTOTAL_HEAP_SIZE                                                     ( 175000 )
#define configMAX_TASK_NAME_LEN                                                   ( 15 )
#define configUSE_16_BIT_TICKS                                                    0
#define configIDLE_SHOULD_YIELD                                                   1
#define configUSE_MUTEXES                                                         1
#define configUSE_RECURSIVE_MUTEXES                                               1
#define configUSE_COUNTING_SEMAPHORES                                             1
#define configUSE_ALTERNATIVE_API                                                 0    /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE                                                 2
#define configUSE_QUEUE_SETS                                                      0
#define configUSE_TIME_SLICING                                                    0
#define configUSE_NEWLIB_REENTRANT                                                0
#define configENABLE_BACKWARD_COMPATIBILITY                                       1
#define configUSE_DAEMON_TASK_STARTUP_HOOK                                        1
#define configSUPPORT_STATIC_ALLOCATION                                           1
#define configUSE_APPLICATION_TASK_TAG                                            1
#define configUSE_POSIX_ERRNO                                                     1
#define configMESSAGE_BUFFER_LENGTH_TYPE                                          uint8_t

I asked two separate tasks to vTaskDelay(1), both show they are resuming early occasionally.

The average INTERVAL, time between the last end and the next start is right at 1ms, which is great. Issue is I have some short and long extremes.

Shortest: 212us
Average: 1.051ms
Longest: 3.305ms

The longest I think I can explain. There are higher priority tasks, it makes sense my intervals can run longer than desired, and this is fine.

How on earth am I calling vTaskDelay(1) and getting a wake pretty reliably at 250-400us? What mechanism would do this? Is there some way to figure out why the scheduler decided a task was ready?

First, you should use vTaskDelay( pdMS_TO_TICKS( 1 ) ); but that should not change anything in this case as configTICK_RATE_HZ is 1000.

In the cases when you get a short interval what was the cause of preemption (last end)? Was it the call to vTaskDelay or something else? Can you share your tracelyzer trace image for one such interval?

Thanks.

Removed pdMs_TO_TICKS because I was pulling out any possible issues. Simplest form for the error.

I can’t share the direct tracealyzer.

But I can answer the question.

TaskA runs and ends with ulTaskNotifyTake(pdTRUE, 10);

If a line edge is detected on a pin, the ISR sets vTaskNotifyGiveFromISR. This is the normal behavior.

Sometimes, if we get to the end of TaskA, but detect there will be more work to do very soon, that is when instead of the NotifyTake I have a conditional vTaskDelay(1) instead. So it’s… if (more_work) delay(1) else notifyTakeAndWaitForISR(block for 10ms).

I had the thought that there was a chance the notify wasn’t being “cleared”, so I looked back to find any interval or indication that saying “block for 10” was “being held on to”. Nothing I could correlate at all.

The “more work” condition is rare, about .5-1% of cycles. But it’s enough to trigger all the time anyhow.

I think I’m on to a solution. It might be a hardware problem, but if it wasn’t - my original questions still stand.

If I am reading it correctly, there are 2 reasons TaskA can block:

  1. Waiting for a notification (which is given from ISR).
  2. vTaskDelay.

What I am trying to ask is - which one is the case when your task is waking up early? In other words, what is the cause of switch out at T1 in the diagram below?

           Task A           Task A
         switched out     switched in
+-------------+---------------+--------------------+
              ^               ^
              |               |
              |               |
              +               +
              T1              T2
              <--------------->
                Block interval

Thanks.

Yea, T1 is the delay.

Normally it’s the notifiytake.

Rarely it’s the delay, and then it wakes less than 1ms.

This is true for TaskA. I also have TaskB that is doing it, and that only has a delay(2) at the end of it’s code. So whatever the reason, it doesn’t seem to be the notify.

T2-T1 can be less than tick if T1 is significantly into the current tick.

1 Tick means when the next tick occurs, which could be almost immediately if you are very late in the tick.

Hmm… That does make sense! Geez. Very simple explanation.

In order for the OS to tick a full or exact or even a close 1ms, it would need it’s actual tick rate to be sub 1ms, the faster the more accurate I can get to 1ms exactly.

I’m setting a delay saying one tick VALUE from now, but the next tick could happen at any time, and definitely less than one full tick away. A delay of 100 ticks is really 99.0001-100 ticks.

Well, I would feel dumb if I didn’t also discover a major issue with ticks and RTC on my hardware. I’ll deal with that next.

Thanks a ton for your help Gaurav and Richard.

I use two different formulas to convert ‘time’ to Ticks. one that rounds the result to the nearest tick value, to be used for things like vTaskDelayUntil, or similar usages, and a second that rounds any fraction up, and adds 1, when I want a delay that will ALWAYS be at least that long. This is for things like Timeouts or waiting to allow for something to happen with a known maximum time.

1 Like

Hello,

I would suggest using vTaskDelayUntil()

The FreeRTOS website informs:
This function differs from vTaskDelay() in one important aspect: vTaskDelay() specifies a time at which the task wishes to unblock relative to the time at which vTaskDelay() is called, whereas vTaskDelayUntil() specifies an absolute time at which the task wishes to unblock.

Regards,
Rajeev