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

Interrupts not waking FreeRTOS up in Tickless mode

Hi

I'm working with FreeRTOS with Tickless (RTC) and I was wondering what one should do in order to avoid the FreeRTOS Idle Task to be waken up every time an Interrupt Service Routine has completed the execution.

In Cortex-M4 the SLEEPONEXIT flag in SCR should help with this feature but I cannot let it work with Nordic. Any hints?

Thanks in Advance.

Parents
  • Hi Davide,

    The tickless idle sleep wakeup only processes to see if there any other pending interrupts to be processed. If there are and if they are lower in priority than the kernel itself, then idle task needs to do the book keeping. 

    Enabling SLEEPONEXIT would keep the state machine of the kernel stuck in the idle loop for ever and might cause asserts in the future. What is it you are trying to achieve exactly? why is the post processing of idle task wakeup seems to be very expensive in your usecase?

  • Thank you Susheel

    waking up the Idle task needs energy. If I could avoid it then I would be very happy.

    I have another project with a Cortex-M4 where I've done the folllowing:

    Let's say I call xTaskGenericNotifyFromISR

    The function delivers the flag pxHigherPriorityTaskWoken.

    I then check this flag in my ISR and, if set, I reset the SLEEPONEXIT flag. This ensures that the Idle task is woken up in order to perform the book keeping you were talking about.

    If the pxHigherPriorityTaskWoken flag is not set, I let the SLEEPONEXIT flag set and the processor will go directly in Sleep after the ISR, thus saving useless energy, because the Idle Task would do just nothing.

    This is very needed also because, if you Instrument the vPortSuppressTicksAndSleep, you will notice that it is executed always twice before it can go really in sleep mode. This is energy that I would like to save.

  • Hi Davide,

     

    dfer said:

    Let's say I call xTaskGenericNotifyFromISR

    The function delivers the flag pxHigherPriorityTaskWoken.

    I then check this flag in my ISR and, if set, I reset the SLEEPONEXIT flag. This ensures that the Idle task is woken up in order to perform the book keeping you were talking about.

     This sounds like a reasonable thing to do, if there are only two tasks in your ssytem (the one giving notification and the other receiving it). But if there are other tasks, and if there is a dependency (inter task) between them, then your idea might not work.

    If only two tasks, then I think this will work.

  • In my other system I have about 30 tasks.

    I forgot to mention that the tick interrupt will always reset the SLEEPONEXIT flag (a Task switch is Always necessary in tickless mode at this Point).

    I cannot imagine why it shouldn't work, the other System runs since months with this method….

  • dfer said:
    I forgot to mention that the tick interrupt will always reset the SLEEPONEXIT flag (a Task switch is Always necessary in tickless mode at this Point).

     This will be a good failsafe mechanism. I think it should work.

  • Hi Susheel do you have any idea why the SLEEPONEXIT functionality doesn't work?

  • I am not sure Davide, it should work, it is an ARM feature and does not seem to be optional.

Reply Children
  • Hi Susheel

    after several attempts it seems that the SPEEPONEXIT functionality is somehow prevented by the softdevice.

    Everything gets stuck after a time and I cannot understand why.

    I would have another suggestion that would prevent the vPortSuppressTicksAndSleep to always be executed twice before managing to go to sleep:

        /* Make sure the SysTick reload value does not overflow the counter. */
        if ( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
        {
            xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP;
        }
        /* Block all the interrupts globally */
    #ifdef SOFTDEVICE_PRESENT
        do{
            uint8_t dummy = 0;
            uint32_t err_code = sd_nvic_critical_region_enter(&dummy);
            APP_ERROR_CHECK(err_code);
        }while (0);
    #else
        __disable_irq();
    #endif
    
        __SEV(); __WFE();// reset the event flag, if an interrupt has occured and has triggered a task it will be cought in eTaskConfirmSleepModeStatus
    
        enterTime = nrf_rtc_counter_get(portNRF_RTC_REG);
    
        if ( eTaskConfirmSleepModeStatus() != eAbortSleep )
        {
            TickType_t xModifiableIdleTime;
            TickType_t wakeupTime = (enterTime + xExpectedIdleTime) & portNRF_RTC_MAXTICKS;
    

    The __SEV(); __WFE(); sequence resets the Event flag. If it was set because an Interrupt has occured, and this Interrupt has triggered a FreeRTOS Task, the subsequent eTaskConfirmSleepModeStatus would detect this and Abort the Sleep sequence.

    The Event flag is Always set because either a Task has been Yielded or an Interrupt has occured while the processor was not sleeping. The first time __WFE(); is executed will almost always go Forward without sleeping, causing another call to vPortSuppressTicksAndSleep form FreeRTOS.

    What do you think about this? I've tested it quickly and it seems working.

  • Or even better here:

        /* Make sure the SysTick reload value does not overflow the counter. */
        if ( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
        {
            xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP;
        }
    
        __SEV(); __WFE();// reset the event flag, if an interrupt has occurred and has triggered a task it will be caught in eTaskConfirmSleepModeStatus
    
        /* Block all the interrupts globally */
    #ifdef SOFTDEVICE_PRESENT
        do{
            uint8_t dummy = 0;
            uint32_t err_code = sd_nvic_critical_region_enter(&dummy);
            APP_ERROR_CHECK(err_code);
        }while (0);
    #else
        __disable_irq();
    #endif
    
        enterTime = nrf_rtc_counter_get(portNRF_RTC_REG);
    
        if ( eTaskConfirmSleepModeStatus() != eAbortSleep )
        {

  • I think __SEV;__WFE;__WFE sequence is a safe and well tested sequence to make the chip go to sleep on the second __WFE. Ofcourse there is a time chance that a new event flag is set after the execution of __SEV and __WFE but that is something we need to live with.

    I am not sure why introducing __SEV;__WFE in the place you suggest improves the possibility of clearing the event flag. There are many instructions from this point to the actual next WFE where there is a great possibility for a new event to enter and raise the event flag.

    In this function can you test the performance by not using sd_app_evt_wait and using __SEV;__WFE;__WFE instead of that.

  • I am not really sure but I see a danger that, if i do as you suggest, an event could be lost.

    The sequence disables the application interrupts (sd_nvic_critical_region_enter), then asks FreeRTOS if there is a task pending, and then sets the RTC up.

    Only after this, the event flag would be cleared. But the interrupts are already disabled, so a possible interrupt could have happened in between and we would clear the related event flag before going to sleep.

    This is the documentation I've found About WFE:

    WFE
    Wait For Event.
    Syntax
    WFE{cond}
    where:
    cond Is an optional condition code, see Conditional execution on page 3-18.
    Operation
    WFE is a hint instruction.
    If the event register is 0, WFE suspends execution until one of the following events occurs:
    • an exception, unless masked by the exception mask registers or the current priority level
    • an exception enters the Pending state, if SEVONPEND in the System Control Register is set
    • a Debug Entry request, if Debug is enabled
    • an event signaled by a peripheral or another processor in a multiprocessor system using 
    the SEV instruction.
    If the event register is 1, WFE clears it to 0 and returns immediately.

  • I would recommend you to read this thread.

    dfer said:
    so a possible interrupt could have happened in between and we would clear the related event flag before going to sleep.

     The FreeRTOS port of Nordic enables SEVONPEND, so If the interrupt happens between the SEV and WFE, then you are right that the event is cleared, but the pend bit is set after one more clock cycle, so that should be ok as far as i can see.

Related