Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Sleep mode does not work correctly with FreeRTOS SDK 15.3?

Hello, 

I'm trying to implement a sleep mode for my application. Currently it has around 8 tasks.

I need to disable them all + disable some HW, such as GPS module. And wait for ISRs to wake it up from either: GSM module or accelerometer.

There are 2 problems currently: 

1. First as it was mentioned in many tickets before f.e. here, that sleep mode actually does not sleep when all tasks are disabled. 
Device wakes up constantly for unknown event/ISR and goes back to sleep. 
It seems that SD is actually doing some job and constantly wakes up here:

            if (nrf_sdh_is_enabled())
            {
                uint32_t err_code = sd_app_evt_wait();
                APP_ERROR_CHECK(err_code);
            }

In addition, WDG is enabled and even though it configured to freeze in sleep mode, soon it will reset the system.
Additional info:  
- configUSE_TICKLESS_IDLE is enabled

- also SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; instruction is used to indicate DeepSleep

Is there something else that needs to be done to have a correct behaviour in deep sleep mode ?

Here is trace record from SystemView:

After all tasks were suspended ISR triggers constantly after 1-2 ms.

2. Since all tasks use xTaskDelayUntil(), when there is a wakeup ISR that returns system back to normal. Call to
xTaskResume(), makes all those

tasks to execute immediately for all that time that was skipped, which of course breaks the system. Thus the question is what is the correct way to put FreeRTOS system in deep sleep?

Parents
  • Update on the ticket problem 1. 

    I have found that Nordic's port  of function portSUPPRESS_TICKS_AND_SLEEP() does not provide handling for eNoTasksWaitingTimeout system state. Thus I have implemented my own function adding such a case as recommended in FreeRTOS manual , using Nordic's code as a basis:

    /* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */
    void vApplicationSleep( TickType_t xExpectedIdleTime )
    {
        /*
         * Implementation note:
         *
         * To help debugging the option configUSE_TICKLESS_IDLE_SIMPLE_DEBUG was presented.
         * This option would make sure that even if program execution was stopped inside
         * this function no more than expected number of ticks would be skipped.
         *
         * Normally RTC works all the time even if firmware execution was stopped
         * and that may lead to skipping too much of ticks.
         */
        TickType_t enterTime;
        eSleepModeStatus eSleepStatus;
    
        /* 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
    
        enterTime = nrf_rtc_counter_get(portNRF_RTC_REG);
    
        /* Ensure it is still ok to enter the sleep mode. */
        eSleepStatus = eTaskConfirmSleepModeStatus();
    
        if ( eSleepStatus == eStandardSleep )
        {
            TickType_t xModifiableIdleTime;
            TickType_t wakeupTime = (enterTime + xExpectedIdleTime) & portNRF_RTC_MAXTICKS;
    
            /* Stop tick events */
            nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
    
            /* Configure CTC interrupt */
            nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime);
            nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0);
            nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK);
    
            __DSB();
    
            /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
             * set its parameter to 0 to indicate that its implementation contains
             * its own wait for interrupt or wait for event instruction, and so wfi
             * should not be executed again.  However, the original expected idle
             * time variable must remain unmodified, so a copy is taken. */
            xModifiableIdleTime = xExpectedIdleTime;
            configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
            if ( xModifiableIdleTime > 0 )
            {
    #ifdef SOFTDEVICE_PRESENT
                if (nrf_sdh_is_enabled())
                {
                    uint32_t err_code = sd_app_evt_wait();
                    APP_ERROR_CHECK(err_code);
                }
                else
    #endif
                {
                    /* No SD -  we would just block interrupts globally.
                    * BASEPRI cannot be used for that because it would prevent WFE from wake up.
                    */
                    do{
                        __WFE();
                    } while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));
                }
            }
            configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
    
            nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK);
            nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0);
    
            /* Correct the system ticks */
            {
                TickType_t diff;
                TickType_t exitTime;
    
                nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_TICK);
                nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
    
                exitTime = nrf_rtc_counter_get(portNRF_RTC_REG);
                diff =  (exitTime - enterTime) & portNRF_RTC_MAXTICKS;
    
                /* It is important that we clear pending here so that our corrections are latest and in sync with tick_interrupt handler */
                NVIC_ClearPendingIRQ(portNRF_RTC_IRQn);
    
                if ((configUSE_TICKLESS_IDLE_SIMPLE_DEBUG) && (diff > xExpectedIdleTime))
                {
                    diff = xExpectedIdleTime;
                }
    
                if (diff > 0)
                {
                    vTaskStepTick(diff);
                }
            }
        }
        else if(eSleepStatus == eNoTasksWaitingTimeout)
        {
            /* Stop tick events */
            nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
            CM_LOG_DEBUG("Deep sleep");
    
            __DSB();
    
    #ifdef SOFTDEVICE_PRESENT
            if (nrf_sdh_is_enabled())
            {
                uint32_t err_code = sd_app_evt_wait();
                APP_ERROR_CHECK(err_code);
            }
            else
    #endif
            {
                /* No SD -  we would just block interrupts globally.
                * BASEPRI cannot be used for that because it would prevent WFE from wake up.
                */
                do{
                    __WFE();
                } while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));
            }
    
            nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
    
            NRF_WDT->RR[0] = 0x6E524635;	//this value must be written in all enabled reload registers (ref. manual p112)
        }
        else
        {
            /* No implementation */
        }
    #ifdef SOFTDEVICE_PRESENT
        uint32_t err_code = sd_nvic_critical_region_exit(0);
        APP_ERROR_CHECK(err_code);
    #else
        __enable_irq();
    #endif
    }


    I can confirm now, that it was softdevice that constantly wakes up system every 2ms, because if I disable it, I don't see any activity.
    Thus, my questions is whether it is expected behaviour from soft device to wake up every 2 ms? Should there be some command issue to soft device to put it into low power? 
    Currently the only thing that is used related to SD is function call to APP_ERROR_CHECK(nrf_sdh_enable_request()). When I remove it system sleeps normally.



  • that is interesting and thanks for letting us no about the eNoTasksWaitingTimeout.

    Regarding the softdevice waking up every 2ms, are you use that you are using sd_app_evt_wait in the portSUPRESS_TICKS_AND_SLEEP or are you using __WFE?

    What HFCLK/LFCLK configuration are you using are you using? 

  • After all tasks were suspended ISR triggers constantly after 1-2 ms.

    The Systemview shows that this is ISR 33 which is PWM. Seems like PWM is configured to generate interrupt on your system which has nothing to do with FreeRTOS port. Can you check why ISR 33 is being fired? This ISR is app owned and not softdevice owned.

  • Hello Susheel, I have figured out what was the cause of interrupts. I used sd_nvic_GetPendingIRQ() to find which IRQs are pending before going to sleep. 


    There were 2 IRQs pending: CRYPTOCELL_IRQn and FPU_IRQn. Thus, this line 

    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

    does not solve FPU pending problem that was present in SDK 14. 

    So I disabled it manualy as was recommended in other tickets and also disabled CRYPTOCELL_IRQn, and it sleeps properly.

    My question now is why CRYPTOCELL_IRQn is pending and what is the way to control it using crypto library. We do use Crypto in our project for some opeartions. But it is used only a couple of times and all operations are considered done before going to sleep. 
    When browsing through Crypto lib description on Nordic info zone I didn't find any info about Crypto ISRs, so I assume it should be controlled somehow by libraries internally. Currenlty I just clear it and disable it before sleep. But is there a more better way to handle it?

    Concerning ISR33, I didn't invetigate it to much, since I found the issue. However, I believe it is not connected with PWM. I assume it is somehow reserved by FreeRTOS and used as a tick ISR for context switch...

     

  • There is a limitation on that the interrupt variant cc310 does not work correctly with RTOS threads.So for now, it is not possible to use the interrupt variant of this library with FreeRTOS.

    You need to remove the existing libnrf_cc310_0.9.13.a from your project

    Added the same from the ..\external\nrf_cc310\lib\cortex-m4\hard-float\no-interrupts\libnrf_cc310_0.9.13.a

     That will make the Crypto to function normally without generating interrupts.

  • maybe use a soft-float variant if you do not want to use FPU

  • Thanks Susheel, I have just checked and we actaully using "no-interrupts" version, but CRYPTOCELL_IRQn is still pending. I think we will solve it by manually clearing pending ISR, before going to sleep as mentioned in this ticket.

    As for FPU, instead of going for soft-float. Can we just disable and enable FPU ISR's before and after sleep respectively? Will crypto work correctly after these operations?

Reply
  • Thanks Susheel, I have just checked and we actaully using "no-interrupts" version, but CRYPTOCELL_IRQn is still pending. I think we will solve it by manually clearing pending ISR, before going to sleep as mentioned in this ticket.

    As for FPU, instead of going for soft-float. Can we just disable and enable FPU ISR's before and after sleep respectively? Will crypto work correctly after these operations?

Children
  • Update. So I have tried to compile with soft float. But it seems that Nordic does not provied possibility to use FreeRTOS with soft-float, which causes error in ..\external\freertos\portable\CMSIS\nrf52\port_cmsis.c

    #error This port can only be used when the project options are configured to enable hardware floating point support.

    So there is no way to use spft-float with FreeRTOS?
  • I think you can safely remove that #error line. 

    It is there for legacy reasons and I am positive that removing that line will have no effect on how FreeRTOS works.

    Please try it and let me know. I have tested removing that line on other different things and it worked ok.

  • Hello Susheel, I have found a different solution. Using critical section 

    portENTER_CRITICAL();

    during crypto operations allows both to execute crypto operations correctly and enter both regular sleep mode and deep sleep, cleaning pending Crypto and FPU IQRs before sleep mode, correclty as well. Thus, I will not test compilation of soft-float library.

    Here is a final code sample of custom implemented 

    portSUPPRESS_TICKS_AND_SLEEP():


    /* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */
    void vApplicationSleep( TickType_t xExpectedIdleTime )
    {
        /*
         * Implementation note:
         *
         * To help debugging the option configUSE_TICKLESS_IDLE_SIMPLE_DEBUG was presented.
         * This option would make sure that even if program execution was stopped inside
         * this function no more than expected number of ticks would be skipped.
         *
         * Normally RTC works all the time even if firmware execution was stopped
         * and that may lead to skipping too much of ticks.
         */
        TickType_t enterTime;
        eSleepModeStatus eSleepStatus;
    
        /* 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
    
        enterTime = nrf_rtc_counter_get(portNRF_RTC_REG);
    
        /* Ensure it is still ok to enter the sleep mode. */
        eSleepStatus = eTaskConfirmSleepModeStatus();
    
        if ( eSleepStatus == eStandardSleep )
        {
            TickType_t xModifiableIdleTime;
            TickType_t wakeupTime = (enterTime + xExpectedIdleTime) & portNRF_RTC_MAXTICKS;
    
            /* Stop tick events */
            nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
    
            /* Configure CTC interrupt */
            nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime);
            nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0);
            nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK);
    
            __DSB();
    
            /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
             * set its parameter to 0 to indicate that its implementation contains
             * its own wait for interrupt or wait for event instruction, and so wfi
             * should not be executed again.  However, the original expected idle
             * time variable must remain unmodified, so a copy is taken. */
            xModifiableIdleTime = xExpectedIdleTime;
            configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
            if ( xModifiableIdleTime > 0 )
            {
    #ifdef SOFTDEVICE_PRESENT
                if (nrf_sdh_is_enabled())
                {
                    uint32_t err_code = sd_app_evt_wait();
                    APP_ERROR_CHECK(err_code);
                }
                else
    #endif
                {
                    /* No SD -  we would just block interrupts globally.
                    * BASEPRI cannot be used for that because it would prevent WFE from wake up.
                    */
                    do{
                        __WFE();
                    } while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));
                }
            }
            configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
    
            nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK);
            nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0);
    
            /* Correct the system ticks */
            {
                TickType_t diff;
                TickType_t exitTime;
    
                nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_TICK);
                nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
    
                exitTime = nrf_rtc_counter_get(portNRF_RTC_REG);
                diff =  (exitTime - enterTime) & portNRF_RTC_MAXTICKS;
    
                /* It is important that we clear pending here so that our corrections are latest and in sync with tick_interrupt handler */
                NVIC_ClearPendingIRQ(portNRF_RTC_IRQn);
    
                if ((configUSE_TICKLESS_IDLE_SIMPLE_DEBUG) && (diff > xExpectedIdleTime))
                {
                    diff = xExpectedIdleTime;
                }
    
                if (diff > 0)
                {
                    vTaskStepTick(diff);
                }
            }
        }
        else if(eSleepStatus == eNoTasksWaitingTimeout)
        {
            /* Stop tick events */
            nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
            CM_LOG_DEBUG("Deep sleep");
    
            __set_FPSCR(__get_FPSCR() & ~(0x0000009F));
            (void) __get_FPSCR();
            NVIC_ClearPendingIRQ(FPU_IRQn);
    
            sd_nvic_ClearPendingIRQ(CRYPTOCELL_IRQn);
    
            __DSB();
    
    #ifdef SOFTDEVICE_PRESENT
            if (nrf_sdh_is_enabled())
            {
                CM_LOG_DEBUG("SD Deep sleep");
                uint32_t err_code = sd_app_evt_wait();
                APP_ERROR_CHECK(err_code);
            }
            else
    #endif
            {
                /* No SD -  we would just block interrupts globally.
                * BASEPRI cannot be used for that because it would prevent WFE from wake up.
                */
                do{
                    __WFE();
                } while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));
            }
    
            nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
    
            NRF_WDT->RR[0] = 0x6E524635;	//this value must be written in all enabled reload registers (ref. manual p112)
        }
        else
        {
            /* No implementation */
        }
    #ifdef SOFTDEVICE_PRESENT
        uint32_t err_code = sd_nvic_critical_region_exit(0);
        APP_ERROR_CHECK(err_code);
    #else
        __enable_irq();
    #endif
    }

    Note! 

    configPRE_SLEEP_PROCESSING() also has instructions that clear pending FPU and Crypto IRQs.


    Should the second part of the questions be discussed here, or should I create a separate thread for it ?

Related