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.

  • 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.

  • No I didn't mean that. I meant between sd_nvic_critical_region_enter and __SEV()

    In my opinion this Interrupt would be "lost". Yes, the pending bit would stay set, but the Idle Task wouldn't be woken up after the second __WFE because the Event flag is cleared.

    I don't think that the Event flag would be set again if the Interrupt is still pending, I think it is a one shot Operation (set Interrupt pending -> set Event flag, but only once).

  • You are right, that seems risky. But I think I am deviating a bit from the original discussion.

    Your original suggestion to have __SEV; __WFE before just entering the critical_section is unnecessary since an event/interrupt can happen just after that.

    Do you have any benchmark results that can show that the current sleep implementation makes it execute twice before actually going to sleep?

    If that is really happening, this discussion would be very serious. Initial tests i did does not show what you wrote. But i have not tested extensively. 

  • Yes as said it you set a debug port before going to sleep and then reset ti immediately after WFE you'll see that the function is always executed twice.

    Back to your comment "Your original suggestion to have __SEV; __WFE before just entering the critical_section is unnecessary since an event/interrupt can happen just after that."

    this isn't right, since I would do the SEV/WFE before  sd_nvic_critical_region_enter and later I would call only WFE (not SEV) thus no interrupts/events would be lost. If an interrupt occurs after sd_nvic_critical_region_enter, it would set the event flag, thus preventing the processor to go to sleep at the next WFE

Reply
  • Yes as said it you set a debug port before going to sleep and then reset ti immediately after WFE you'll see that the function is always executed twice.

    Back to your comment "Your original suggestion to have __SEV; __WFE before just entering the critical_section is unnecessary since an event/interrupt can happen just after that."

    this isn't right, since I would do the SEV/WFE before  sd_nvic_critical_region_enter and later I would call only WFE (not SEV) thus no interrupts/events would be lost. If an interrupt occurs after sd_nvic_critical_region_enter, it would set the event flag, thus preventing the processor to go to sleep at the next WFE

Children
  • I see your point but 

    dfer said:
    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.

    I see that the first one (Event set due to task yield) is not a problem, since the scheduler needs to be called before sleeping which is correct behavior that the sleep is aborted. You should not at this point try to clear that event that is the result of any state change of the task, this could cause deadlocks.

    But I see that you have a point that if there is an interrupt  happening inbetween the critical section, then the vPortSuppressTicksAndSleep code unnecessarily exits the sleep routine.

    Wow, this is a good find. I am sorry that i missed to see the details of your previous reply. An interrupt could occur even while in critical section setting the event flag, so I am not sure that just clearing the event before entering critical section helps that much??

  • I feel we are a bit drifting from each other away.

    So to recap:

    any Task switch sets the Event flag with __SEV, see portYIELD macro (why? I guess  SCB->ICSR = SCB_ICSR_PENDSVSET_Msk would be enough, the PendSV interrupt would be set pending and the event flag is set pending, see SEVONPEND) 

    the tick interrupt sets the event flag with __SEV (why? see comment above)

    any Interrupt sets the Event flag automatically (see SEVONPEND)

    So the chances that we come to vPortSuppressTicksAndSleep with the event flag already set are high. This event flag setting has happened most probably in the past and the scheduler has already served the necessary task switches. 

    Now, resetting the event flag before the sd_nvic_critical_region_enter function call should be ok, because the ISR of an interrupt happening just before sd_nvic_critical_region_enter would be anyway invoked. If this ISR(*) triggers a FreeRTOS task, the scheduler would set that task to Ready.

    Now the sd_nvic_critical_region_enter  is called. 

    After this, a masked application interrupt sets the event flag but the ISR is not executed. 

    After sd_nvic_critical_region_enter, the function eTaskConfirmSleepModeStatus is called, which will abort the sleep process if a task or a task switch is pending (see served ISR above(*) ) - so here I see no problems.

    If an interrupt happens after eTaskConfirmSleepModeStatus, it will set the event flag and the next WFE will be skipped - here I see no problem.

    If an interrupt occurs after WFE, the normal wake up prcedure will happen and again here I see no problems.

  • dfer said:
    any Task switch sets the Event flag with __SEV, see portYIELD macro (why? I guess  SCB->ICSR = SCB_ICSR_PENDSVSET_Msk would be enough, the PendSV interrupt would be set pending and the event flag is set pending, see SEVONPEND) 

    You are right. I think SEV is not needed here, but I think it is a harmless addition. SEVONPEND bit set does not need SEV to be added explicitly after pending this to ICSR.

    dfer said:
    So the chances that we come to vPortSuppressTicksAndSleep with the event flag already set are high. This event flag setting has happened most probably in the past and the scheduler has already served the necessary task switches. 

    Agreed...

    dfer said:
    After this, a masked application interrupt sets the event flag but the ISR is not executed. 

    Agreed, because of SEVONPEND. 

     

    dfer said:
    After sd_nvic_critical_region_enter, the function eTaskConfirmSleepModeStatus is called, which will abort the sleep process if a task or a task switch is pending (see served ISR above(*) ) - so here I see no problems.

     Agreed.

     

    dfer said:
    If an interrupt happens after eTaskConfirmSleepModeStatus, it will set the event flag and the next WFE will be skipped - here I see no problem.

     Agreed.

     

    dfer said:
    If an interrupt occurs after WFE, the normal wake up prcedure will happen and again here I see no problems.

     Agreed.

    i agree with you Davide, i missed to see the check eTaskConfirmSleepModeStatus. Should be safe to clear the event just before entering the critical section.

Related