From 9caaf260129731e4ca5e2ee9ef3ba1d9251c354b Mon Sep 17 00:00:00 2001 From: Atis Elsts Date: Mon, 27 Jun 2016 19:48:15 +0300 Subject: [PATCH] CC26xx: fix a regression in and refactor LPM code --- cpu/cc26xx-cc13xx/lpm.c | 178 +++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 65 deletions(-) diff --git a/cpu/cc26xx-cc13xx/lpm.c b/cpu/cc26xx-cc13xx/lpm.c index 3253202c2..78aa2c764 100644 --- a/cpu/cc26xx-cc13xx/lpm.c +++ b/cpu/cc26xx-cc13xx/lpm.c @@ -83,7 +83,7 @@ LIST(modules_list); #define SLEEP_GUARD_TIME (RTIMER_SECOND / 1000) /* 1.0 ms */ #define MAX_SLEEP_TIME RTIMER_SECOND -#define MINIMAL_SAFE_SCHEDULE 8u +#define MIN_SAFE_SCHEDULE 8u /*---------------------------------------------------------------------------*/ /* Prototype of a function in clock.c. Called every time we come out of DS */ void clock_update(void); @@ -251,71 +251,71 @@ wake_up(void) #endif } /*---------------------------------------------------------------------------*/ -static int -setup_sleep_mode(rtimer_clock_t *next_timer) +static uint8_t +check_next_rtimer(rtimer_clock_t now, rtimer_clock_t *next_rtimer, bool *next_rtimer_set) +{ + uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; + + if(ti_lib_aon_rtc_channel_active(AON_RTC_CH0)) { + *next_rtimer_set = true; + + /* find out the timer of the next rtimer interrupt */ + *next_rtimer = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH0); + + if(RTIMER_CLOCK_LT(*next_rtimer, now + 2)) { + max_pm = MIN(max_pm, LPM_MODE_AWAKE); + } else if(RTIMER_CLOCK_LT(*next_rtimer, now + STANDBY_MIN_DURATION)) { + max_pm = MIN(max_pm, LPM_MODE_SLEEP); + } + } else { + *next_rtimer_set = false; + } + + return max_pm; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +check_next_etimer(rtimer_clock_t now, rtimer_clock_t *next_etimer, bool *next_etimer_set) +{ + uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; + + *next_etimer_set = false; + + /* Find out the time of the next etimer */ + if(etimer_pending()) { + int32_t until_next_etimer = (int32_t)etimer_next_expiration_time() - (int32_t)clock_time(); + if(until_next_etimer < 1) { + max_pm = MIN(max_pm, LPM_MODE_AWAKE); + } else { + *next_etimer_set = true; + *next_etimer = soc_rtc_last_isr_time() + (until_next_etimer * (RTIMER_SECOND / CLOCK_SECOND)); + if(RTIMER_CLOCK_LT(*next_etimer, now + STANDBY_MIN_DURATION)) { + max_pm = MIN(max_pm, LPM_MODE_SLEEP); + } + } + } + + return max_pm; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +setup_sleep_mode(void) { lpm_registered_module_t *module; uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; + uint8_t pm; - rtimer_clock_t now = RTIMER_NOW(); - const rtimer_clock_t max_sleep = now + MAX_SLEEP_TIME; - - /* next_timer will hold the time of the next system wakeup due to a timer*/ - *next_timer = max_sleep; + rtimer_clock_t now; + rtimer_clock_t next_rtimer = 0; + rtimer_clock_t next_etimer = 0; + bool next_rtimer_set = false; + bool next_etimer_set = false; /* Check if any events fired before we turned interrupts off. If so, abort */ if(LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE || process_nevents()) { return LPM_MODE_AWAKE; } - if(ti_lib_aon_rtc_channel_active(AON_RTC_CH0)) { - rtimer_clock_t next_rtimer; - /* find out the timer of the next rtimer interrupt */ - next_rtimer = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH0); - if(RTIMER_CLOCK_LT(next_rtimer, now + 2)) { - return LPM_MODE_AWAKE; - } - if(RTIMER_CLOCK_LT(next_rtimer, now + STANDBY_MIN_DURATION)) { - return LPM_MODE_SLEEP; - } - *next_timer = next_rtimer; - } - - /* also find out the timer of the next etimer */ - if(etimer_pending()) { - int32_t until_next_etimer; - rtimer_clock_t next_etimer; - - until_next_etimer = (int32_t)etimer_next_expiration_time() - (int32_t)clock_time(); - if(until_next_etimer < 1) { - return LPM_MODE_AWAKE; - } - - next_etimer = soc_rtc_last_isr_time() + (until_next_etimer * (RTIMER_SECOND / CLOCK_SECOND)); - if(RTIMER_CLOCK_LT(next_etimer, now + STANDBY_MIN_DURATION)) { - /* ensure that we schedule sleep a minimal number of ticks into the - future */ - soc_rtc_schedule_one_shot(AON_RTC_CH1, now + MINIMAL_SAFE_SCHEDULE); - return LPM_MODE_SLEEP; - } - - if(RTIMER_CLOCK_LT(max_sleep, next_etimer)) { - /* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept - for too long. */ - if(RTIMER_CLOCK_LT(max_sleep, *next_timer)) { - soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep); - } - } else { - /* Reschedule AON RTC CH1 to fire just in time for the next etimer event */ - soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer); - } - - if(RTIMER_CLOCK_LT(next_etimer, *next_timer)) { - /* set `next_timer` to the time the first etimer fires */ - *next_timer = next_etimer; - } - } - /* Collect max allowed PM permission from interested modules */ for(module = list_head(modules_list); module != NULL; module = module->next) { @@ -327,6 +327,60 @@ setup_sleep_mode(rtimer_clock_t *next_timer) } } + now = RTIMER_NOW(); + + pm = check_next_rtimer(now, &next_rtimer, &next_rtimer_set); + if(pm < max_pm) { + max_pm = pm; + } + pm = check_next_etimer(now, &next_etimer, &next_etimer_set); + if(pm < max_pm) { + max_pm = pm; + } + + if(max_pm == LPM_MODE_SLEEP) { + if(next_etimer_set) { + /* Schedule the next system wakeup due to etimer */ + if(RTIMER_CLOCK_LT(next_etimer, now + MIN_SAFE_SCHEDULE)) { + /* Too soon in future, use this minimal interval instead */ + next_etimer = now + MIN_SAFE_SCHEDULE; + } else if(RTIMER_CLOCK_LT(now + MAX_SLEEP_TIME, next_etimer)) { + /* Too far in future, use MAX_SLEEP_TIME instead */ + next_etimer = now + MAX_SLEEP_TIME; + } + soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer); + } else { + /* No etimers set. Since by default the CH1 RTC fires once every clock tick, + * need to explicitly schedule a wakeup in the future to save energy. + * But do not stay in this mode for too long, otherwise watchdog will be trigerred. */ + soc_rtc_schedule_one_shot(AON_RTC_CH1, now + MAX_SLEEP_TIME); + } + + } else if(max_pm == LPM_MODE_DEEP_SLEEP) { + /* Watchdog is not enabled, so deep sleep can continue an arbitrary long time. + * On the other hand, if `CC2650_FAST_RADIO_STARTUP` is defined, + * early wakeup before the next rtimer should be scheduled. */ + +#if CC2650_FAST_RADIO_STARTUP + if(next_rtimer_set) { + if(!next_etimer_set || RTIMER_CLOCK_LT(next_rtimer - SLEEP_GUARD_TIME, next_etimer)) { + /* schedule a wakeup briefly before the next rtimer to wake up the system */ + soc_rtc_schedule_one_shot(AON_RTC_CH2, next_rtimer - SLEEP_GUARD_TIME); + } + } +#endif + + if(next_etimer_set) { + /* Schedule the next system wakeup due to etimer. + * No need to compare the `next_etimer` to `now` here as this branch + * is only entered when there's sufficient time for deep sleeping. */ + soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer); + } else { + /* Use the farthest possible wakeup time */ + soc_rtc_schedule_one_shot(AON_RTC_CH1, now - 1); + } + } + return max_pm; } /*---------------------------------------------------------------------------*/ @@ -350,16 +404,11 @@ lpm_sleep(void) } /*---------------------------------------------------------------------------*/ static void -deep_sleep(rtimer_clock_t next_timer) +deep_sleep(void) { uint32_t domains = LOCKABLE_DOMAINS; lpm_registered_module_t *module; -#if CC2650_FAST_RADIO_STARTUP - /* schedule a wakeup briefly before the next etimer/rtimer to wake up the system */ - soc_rtc_schedule_one_shot(AON_RTC_CH2, next_timer - SLEEP_GUARD_TIME); -#endif - /* * Notify all registered modules that we are dropping to mode X. We do not * need to do this for simple sleep. @@ -490,18 +539,17 @@ void lpm_drop() { uint8_t max_pm; - rtimer_clock_t next_timer; /* Critical. Don't get interrupted! */ ti_lib_int_master_disable(); - max_pm = setup_sleep_mode(&next_timer); + max_pm = setup_sleep_mode(); /* Drop */ if(max_pm == LPM_MODE_SLEEP) { lpm_sleep(); } else if(max_pm == LPM_MODE_DEEP_SLEEP) { - deep_sleep(next_timer); + deep_sleep(); } ti_lib_int_master_enable();