This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

FreeRTOS timers may fire earlier than expected when configUSE_TICKLESS_IDLE == 1

Hi all,
I am using nrf52 with Softdevice 113 v7.0.1 and SDKv16 and FreeRTOS v10.0.1 and I am experiencing some issues with FreeRTOS timers expiring earlier than expected when configUSE_TICKLESS_IDLE == 1.

The problems started occurring when I applied fix in vTaskStepTick() proposed in the FreeRTOS forum: https://forums.freertos.org/t/assert-in-tasks-c-2611-on-xtickcount-wrap-around/9984
There were two solutions proposed, one in the Nordic port and the seconds in generic FreeRTOS code, but both of them cause the same problem.

I also created the corresponding post in the FreeRTOS forum: forums.freertos.org/.../13416

So is there any solution that fixes the original problem with assert on xTickCount wrap around that will not cause timers to expire too early?

Parents
  • Hi,

    Sorry for the late reply, I was travelling and did not get a chance. It is interesting problem you have and I have seen the fix proposed and I like the idea but i think the fix needs a minor modification.

    Current Code:

                if (diff > 0)
                {
                    vTaskStepTick(diff);
                }

    freertos forum Proposed Code:

                if (diff > 0)
                {
                    vTaskStepTick(diff - 1);
                    NVIC_SetPendingIRQ(portNRF_RTC_IRQn);
                }


    New proposed code
                if (diff > 0)
                {
                    BaseType_t switch_req = pdFALSE;
    
                    vTaskStepTick(diff - 1);
                    switch_req |= xTaskIncrementTick();
    
                    /* Increment the RTOS tick as usual which checks if there is a need for rescheduling */
                    if ( switch_req != pdFALSE )
                    {
                        /* A context switch is required.  Context switching is performed in
                        the PendSV interrupt.  Pend the PendSV interrupt. */
                        SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
                        __SEV();
                    }
                }



  • Hi, thanks for the response and the proposed fix. 
    Actually, there are two proposed fixes in the freeRTOS forum. 
    The first one is in function vPortSuppressTicksAndSleep() and it looks exactly like you posted, but there is also the second fix placed in tasks.c in freeRTOS code. 
    I once again tried to reproduce this bug on all fixes including yours, and here are the results:
    (The tests I performed was to trigger pin state change every 20ms for 10 minutes and observe the minimum and maximum pulse time)
    1. Original fix in vPortSuppressTicksAndSleep(): min = 17.5ms, max = 22.5ms
    2. Original fix in tasks.c: min = 18.5ms, max = 21.5ms
    3. FIx proposed by You: min = 18.5ms, max = 21.5ms
    4. configUSE_TICKLESS_IDLE = 0, min = 19.4ms, max=20.65ms

    So it looks like your proposition is better than the original fix in vPortSuppressTicksAndSleep() but I am not sure is it good enough.
    Is it possible to achieve the timings exactly as for USE_TICKLESS_IDLE = 0? 

  • I also confirmed that with configUSE_TICKLESS_IDLE = 1 and without fixes applied, those timings are also min = 19.5ms and max = 20.5ms.
    So the conclusion is that all those fixes make it work worse. 

Reply Children
  • Then the issue is definitely inside the vPortSuppressTicksAndSleep. This seems something that we need to identify and fix. 

    As far as I can see, there is a bug somewhere in these two possible scenarios
    1. xExpectedIdleTime in vPortSuppressTicksAndSleep is somehow handled wrongly, since we disable the RTC interrupts for the time based on this value, it might be possible that the device is sleeping more than it should even if the timers are supposed to be expired earlier. Also disable the NRF_LOGS, just to be sure as this logger_thread interferes with the idle thread and I want to rule out logger_threads role in our next experiments. Try setting configUSE_TICKLESS_IDLE_SIMPLE_DEBUG to 0 and do some tests. My thinking here is that maybe the internal RTOS ticks got diverged in this correction if something happened to the sleep wakeup time.
    2. The wakeup from the sleep in 
      vPortSuppressTicksAndSleep is taking aweful lot of time. This seems less likely but still possible. Not sure what we could optimize if this is what it is happening.
  • xExpectedIdleTime in vPortSuppressTicksAndSleep is somehow handled wrongly, since we disable the RTC interrupts for the time based on this value, it might be possible that the device is sleeping more than it should even if the timers are supposed to be expired earlier.

    and

    The wakeup from the sleep in 
    vPortSuppressTicksAndSleep is taking aweful lot of time. This seems less likely but still possible. Not sure what we could optimize if this is what it is happening.

    as far as I understand it, both should cause the timer to expire later than expected. 
    And for me, it is not such a big problem. 
    I am more concerned about the timer expiring earlier than expected. 
    But anyway, I will redo the tests with configUSE_TICKLESS_IDLE_SIMPLE_DEBUG = 0 and let you know about the results

  • I just checked this fix combined with configUSE_TICKLESS_IDLE_SIMPLE_DEBUG = 0 and the results are the same: min = 18.5ms, max = 21.5ms

  • I will have to reproduce this. I will make a small project to test the accuracy of the timers and come back to you today

  • Hi, I have tried to reproduce the 17.5ms min and 22.5 ms max variation of toggles at my desk and I was not successful in that. Which makes me think that there could be probably something more in your application that might be masking the kernel for longer times during some operations. One thing that i can think of such long operations can be flash erase operations. Are you using FDS library actively in your application? if so that can explain some shadowing while using using sd_app_evt_wait. 

    Do you see the same results when you use below version of the sleep instead of sd_app_evt_wait?

                    do{
                        __WFE();
                    } while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));

Related