From 08fddb6598aacb3f7a5424ff79bfe548407c4d37 Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Mon, 9 Nov 2015 13:28:52 +0000 Subject: [PATCH 1/2] Always set a valid time for the next AON RTC CH1 compare event The AON RTC CH1 event handler aims to schedule the next compare event on the next 512 RTC counter boundary. However, the current calculation of "now" takes place too early within the interrupt handler. In some cases, this results in the next event getting scheduled too soon in the future or on some extreme cases even in the past. AON RTC compare events cannot happen within 2 SCLK_LF cycles after a clearance (4 RTC ticks in the 16.16 format). Thus, if the next 512 boundary is too soon (5 ticks for margin), we skip it altogether. When this happens, etimers that would have expired on the skipped tick will expire 1 tick later instead. Skipping a tick has no negative impact on our s/w clock counter, since this is always derived directly from the hardware counter. --- cpu/cc26xx-cc13xx/dev/soc-rtc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpu/cc26xx-cc13xx/dev/soc-rtc.c b/cpu/cc26xx-cc13xx/dev/soc-rtc.c index fb5394947..08d9a7cce 100644 --- a/cpu/cc26xx-cc13xx/dev/soc-rtc.c +++ b/cpu/cc26xx-cc13xx/dev/soc-rtc.c @@ -142,14 +142,12 @@ soc_rtc_last_isr_time(void) void soc_rtc_isr(void) { - uint32_t now, next; + uint32_t next; ENERGEST_ON(ENERGEST_TYPE_IRQ); last_isr_time = RTIMER_NOW(); - now = ti_lib_aon_rtc_current_compare_value_get(); - /* Adjust the s/w tick counter irrespective of which event trigger this */ clock_update(); @@ -161,7 +159,8 @@ soc_rtc_isr(void) * event on the next 512 tick boundary. If we drop to deep sleep before it * happens, lpm_drop() will reschedule us in the 'distant' future */ - next = (now + COMPARE_INCREMENT) & MULTIPLE_512_MASK; + next = ((ti_lib_aon_rtc_current_compare_value_get() + 5) + + COMPARE_INCREMENT) & MULTIPLE_512_MASK; ti_lib_aon_rtc_compare_value_set(AON_RTC_CH1, next); } From 571cf9364a4e640c956b5a110876c453dd698062 Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Mon, 9 Nov 2015 13:36:11 +0000 Subject: [PATCH 2/2] Improve CC13xx/CC26xx LPM logic This commit applies a number of improvements to the logic used when trying to drop to a CC13xx/CC26xx low-power mode: * We identify whether there are any pending etimers by using `etimer_pending()` instead of `etimer_next_expiration_time()`. This subsequently allows us to also identify whether an etimer is set to fire at time 0. * We run a larger portion of the code with the global interrupt disabled. This prevents a number of messy conditions that can occur if an interrupt fires after we have started the low-power sequence. * We check whether there are pending events earlier in the sequence. * We make sure to schedule a next wakeup event even when an LPM module prohibits deep sleep and forces sleep instead. This fixes some of the issues discussed in #1236 --- cpu/cc26xx-cc13xx/lpm.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/cpu/cc26xx-cc13xx/lpm.c b/cpu/cc26xx-cc13xx/lpm.c index c9e874824..6af58e899 100644 --- a/cpu/cc26xx-cc13xx/lpm.c +++ b/cpu/cc26xx-cc13xx/lpm.c @@ -244,8 +244,18 @@ lpm_drop() uint32_t domains = LOCKABLE_DOMAINS; + /* Critical. Don't get interrupted! */ + ti_lib_int_master_disable(); + + /* Check if any events fired before we turned interrupts off. If so, abort */ + if(process_nevents()) { + ti_lib_int_master_enable(); + return; + } + if(RTIMER_CLOCK_LT(soc_rtc_get_next_trigger(), RTIMER_NOW() + STANDBY_MIN_DURATION)) { + ti_lib_int_master_enable(); lpm_sleep(); return; } @@ -261,30 +271,20 @@ lpm_drop() } } - /* Check if any events fired during this process. Last chance to abort */ - if(process_nevents()) { - return; + /* Reschedule AON RTC CH1 to fire just in time for the next etimer event */ + next_event = etimer_next_expiration_time(); + + if(etimer_pending()) { + next_event = next_event - clock_time(); + soc_rtc_schedule_one_shot(AON_RTC_CH1, soc_rtc_last_isr_time() + + (next_event * (RTIMER_SECOND / CLOCK_SECOND))); } /* Drop */ if(max_pm == LPM_MODE_SLEEP) { + ti_lib_int_master_enable(); lpm_sleep(); } else { - /* Critical. Don't get interrupted! */ - ti_lib_int_master_disable(); - - /* - * Reschedule AON RTC CH1 to fire an event N ticks before the next etimer - * event - */ - next_event = etimer_next_expiration_time(); - - if(next_event) { - next_event = next_event - clock_time(); - soc_rtc_schedule_one_shot(AON_RTC_CH1, soc_rtc_last_isr_time() + - (next_event * (RTIMER_SECOND / CLOCK_SECOND))); - } - /* * Notify all registered modules that we are dropping to mode X. We do not * need to do this for simple sleep.