From aee099cf49c07e538d03dd3699ce612acb98b409 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Thu, 30 Nov 2017 10:44:58 +0100 Subject: [PATCH 1/9] NRF5 - make us ticker to be driven by high speed 1MHz timer According to new ticker standards the following requirements for us ticker are not met on RRF5 boards: - has a frequency between 250KHz and 8MHz (currently is driven by 32kHz clock) - ticker increments by 1 each tick (currently is scaled to 1 MHz by incrementing counter by ~31) Since BLE softdevice uses TIMER0 the proposition is to use high speed TIMER1 for us ticker configured as follows: - TIMER counter width: 16 bits (max) - TIMER frequency: 1MHz This solution also uses Timer's capture/compare register 0 to specify interrupt time and Timer's capture/compare register 1 to read current timer value. --- .../TARGET_NORDIC/TARGET_NRF5/common_rtc.c | 276 +++++++++++++++++ .../TARGET_NORDIC/TARGET_NRF5/common_rtc.h | 12 +- targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.c | 288 +++--------------- targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.h | 26 ++ 4 files changed, 352 insertions(+), 250 deletions(-) create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.h diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c new file mode 100644 index 00000000000..6fdd2c688a8 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2013 Nordic Semiconductor ASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA + * integrated circuit in a product or a software update for such product, must reproduce + * the above copyright notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary or object form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "us_ticker_api.h" +#include "common_rtc.h" +#include "app_util.h" +#include "nrf_drv_common.h" +#include "lp_ticker_api.h" +#include "mbed_critical.h" + +#if defined(NRF52_ERRATA_20) + #include "softdevice_handler.h" +#endif + +//------------------------------------------------------------------------------ +// Common stuff used also by lp_ticker and rtc_api (see "common_rtc.h"). +// +#include "app_util_platform.h" + +bool m_common_rtc_enabled = false; +uint32_t volatile m_common_rtc_overflows = 0; + +__STATIC_INLINE void rtc_ovf_event_check(void) +{ + if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) { + nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + // Don't disable this event. It shall occur periodically. + + ++m_common_rtc_overflows; + } +} + +#if defined(TARGET_MCU_NRF51822) +void common_rtc_irq_handler(void) +#else +void COMMON_RTC_IRQ_HANDLER(void) +#endif +{ + rtc_ovf_event_check(); + +#if DEVICE_LOWPOWERTIMER + if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, LP_TICKER_EVENT)) { + + lp_ticker_irq_handler(); + } +#endif +} + +// Function for fix errata 20: RTC Register values are invalid +__STATIC_INLINE void errata_20(void) +{ +#if defined(NRF52_ERRATA_20) + if (!softdevice_handler_is_enabled()) + { + NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; + NRF_CLOCK->TASKS_LFCLKSTART = 1; + + while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) + { + } + } + NRF_RTC1->TASKS_STOP = 0; +#endif +} + +void RTC1_IRQHandler(void); + +void common_rtc_init(void) +{ + if (m_common_rtc_enabled) { + return; + } + + errata_20(); + + NVIC_SetVector(RTC1_IRQn, (uint32_t)RTC1_IRQHandler); + + // RTC is driven by the low frequency (32.768 kHz) clock, a proper request + // must be made to have it running. + // Currently this clock is started in 'SystemInit' (see "system_nrf51.c" + // or "system_nrf52.c", respectively). + + nrf_rtc_prescaler_set(COMMON_RTC_INSTANCE, 0); + +#if defined(TARGET_MCU_NRF51822) + nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT); +#endif +#if DEVICE_LOWPOWERTIMER + nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT); +#endif + nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + + // Interrupts on all related events are enabled permanently. Particular + // events will be enabled or disabled as needed (such approach is more + // energy efficient). + nrf_rtc_int_enable(COMMON_RTC_INSTANCE, +#if DEVICE_LOWPOWERTIMER + LP_TICKER_INT_MASK | +#endif + NRF_RTC_INT_OVERFLOW_MASK); + + // This event is enabled permanently, since overflow indications are needed + // continuously. + nrf_rtc_event_enable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + // All other relevant events are initially disabled. + nrf_rtc_event_disable(COMMON_RTC_INSTANCE, 0 | +#if defined(TARGET_MCU_NRF51822) + OS_TICK_INT_MASK | +#endif +#if DEVICE_LOWPOWERTIMER + LP_TICKER_INT_MASK +#endif + ); + + nrf_drv_common_irq_enable(nrf_drv_get_IRQn(COMMON_RTC_INSTANCE), +#ifdef NRF51 + APP_IRQ_PRIORITY_LOW +#elif defined(NRF52) || defined(NRF52840_XXAA) + APP_IRQ_PRIORITY_LOWEST +#endif + ); + + nrf_rtc_task_trigger(COMMON_RTC_INSTANCE, NRF_RTC_TASK_START); + + m_common_rtc_enabled = true; +} + +__STATIC_INLINE void rtc_ovf_event_safe_check(void) +{ + core_util_critical_section_enter(); + + rtc_ovf_event_check(); + + core_util_critical_section_exit(); +} + + +uint32_t common_rtc_32bit_ticks_get(void) +{ + uint32_t ticks; + uint32_t prev_overflows; + + do { + prev_overflows = m_common_rtc_overflows; + + ticks = nrf_rtc_counter_get(COMMON_RTC_INSTANCE); + // The counter used for time measurements is less than 32 bit wide, + // so its value is complemented with the number of registered overflows + // of the counter. + ticks += (m_common_rtc_overflows << RTC_COUNTER_BITS); + + // Check in case that OVF occurred during execution of a RTC handler (apply if call was from RTC handler) + // m_common_rtc_overflows might been updated in this call. + rtc_ovf_event_safe_check(); + + // If call was made from a low priority level m_common_rtc_overflows might have been updated in RTC handler. + } while (m_common_rtc_overflows != prev_overflows); + + return ticks; +} + +uint64_t common_rtc_64bit_us_get(void) +{ + uint32_t ticks = common_rtc_32bit_ticks_get(); + // [ticks -> microseconds] + return ROUNDED_DIV(((uint64_t)ticks) * 1000000, RTC_INPUT_FREQ); +} + +void common_rtc_set_interrupt(uint32_t us_timestamp, uint32_t cc_channel, + uint32_t int_mask) +{ + // The internal counter is clocked with a frequency that cannot be easily + // multiplied to 1 MHz, therefore besides the translation of values + // (microsecond <-> ticks) a special care of overflows handling must be + // taken. Here the 32-bit timestamp value is complemented with information + // about current the system up time of (ticks + number of overflows of tick + // counter on upper bits, converted to microseconds), and such 64-bit value + // is then translated to counter ticks. Finally, the lower 24 bits of thus + // calculated value is written to the counter compare register to prepare + // the interrupt generation. + uint64_t current_time64 = common_rtc_64bit_us_get(); + // [add upper 32 bits from the current time to the timestamp value] + uint64_t timestamp64 = us_timestamp + + (current_time64 & ~(uint64_t)0xFFFFFFFF); + // [if the original timestamp value happens to be after the 32 bit counter + // of microsends overflows, correct the upper 32 bits accordingly] + if (us_timestamp < (uint32_t)(current_time64 & 0xFFFFFFFF)) { + timestamp64 += ((uint64_t)1 << 32); + } + // [microseconds -> ticks, always round the result up to avoid too early + // interrupt generation] + uint32_t compare_value = + (uint32_t)CEIL_DIV((timestamp64) * RTC_INPUT_FREQ, 1000000); + + + core_util_critical_section_enter(); + // The COMPARE event occurs when the value in compare register is N and + // the counter value changes from N-1 to N. Therefore, the minimal safe + // difference between the compare value to be set and the current counter + // value is 2 ticks. This guarantees that the compare trigger is properly + // setup before the compare condition occurs. + uint32_t closest_safe_compare = common_rtc_32bit_ticks_get() + 2; + if ((int)(compare_value - closest_safe_compare) <= 0) { + compare_value = closest_safe_compare; + } + + nrf_rtc_cc_set(COMMON_RTC_INSTANCE, cc_channel, RTC_WRAP(compare_value)); + nrf_rtc_event_enable(COMMON_RTC_INSTANCE, int_mask); + + core_util_critical_section_exit(); +} + +// Since there is no SysTick on NRF51, the RTC1 channel 1 is used as an +// alternative source of RTOS ticks. +#if defined(TARGET_MCU_NRF51822) + +#include "mbed_toolchain.h" + + +#define MAX_RTC_COUNTER_VAL ((1uL << RTC_COUNTER_BITS) - 1) + +#ifndef RTC1_CONFIG_FREQUENCY + #define RTC1_CONFIG_FREQUENCY 32678 // [Hz] +#endif + + + +void COMMON_RTC_IRQ_HANDLER(void) +{ + if(!nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) { + common_rtc_irq_handler(); + } +} + +IRQn_Type mbed_get_m0_tick_irqn() +{ + return SWI3_IRQn; +} + + +#endif // defined(TARGET_MCU_NRF51822) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h index 2ebe70ecf72..6e5cefc20d3 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h @@ -22,16 +22,12 @@ #define RTC_COUNTER_BITS 24u // Instance 0 is reserved for SoftDevice. -// Instance 1 is used as a common one for us_ticker, lp_ticker and (in case +// Instance 1 is used as a common one for lp_ticker and (in case // of NRF51) as an alternative tick source for RTOS. -// ["us_ticker.c" uses hard coded addresses of the 'NRF_RTC1->EVENT_COMPARE[1]' -// register in inline assembly implementations of COMMON_RTC_IRQ_HANDLER, -// please remember to update those in case of doing changes here] #define COMMON_RTC_INSTANCE NRF_RTC1 #define COMMON_RTC_IRQ_HANDLER RTC1_IRQHandler -#define US_TICKER_CC_CHANNEL 0 -#define OS_TICK_CC_CHANNEL 1 -#define LP_TICKER_CC_CHANNEL 2 +#define OS_TICK_CC_CHANNEL 0 +#define LP_TICKER_CC_CHANNEL 1 #define US_TICKER_SW_IRQ_MASK 0x1 #define LP_TICKER_SW_IRQ_MASK 0x2 @@ -41,8 +37,6 @@ #define COMMON_RTC_INT_COMPARE_MASK(channel) \ CONCAT_3(NRF_RTC_INT_COMPARE, channel, _MASK) -#define US_TICKER_EVENT COMMON_RTC_EVENT_COMPARE(US_TICKER_CC_CHANNEL) -#define US_TICKER_INT_MASK COMMON_RTC_INT_COMPARE_MASK(US_TICKER_CC_CHANNEL) #define OS_TICK_EVENT COMMON_RTC_EVENT_COMPARE(OS_TICK_CC_CHANNEL) #define OS_TICK_INT_MASK COMMON_RTC_INT_COMPARE_MASK(OS_TICK_CC_CHANNEL) #define LP_TICKER_EVENT COMMON_RTC_EVENT_COMPARE(LP_TICKER_CC_CHANNEL) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.c b/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.c index a341ae2d76f..11e2805b3aa 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.c @@ -37,131 +37,55 @@ */ #include "us_ticker_api.h" -#include "common_rtc.h" -#include "app_util.h" +#include "nrf_timer.h" +#include "app_util_platform.h" #include "nrf_drv_common.h" -#include "lp_ticker_api.h" #include "mbed_critical.h" +#include "us_ticker.h" -#if defined(NRF52_ERRATA_20) - #include "softdevice_handler.h" -#endif - -//------------------------------------------------------------------------------ -// Common stuff used also by lp_ticker and rtc_api (see "common_rtc.h"). -// -#include "app_util_platform.h" - -bool m_common_rtc_enabled = false; -uint32_t volatile m_common_rtc_overflows = 0; - -// lp/us ticker fire interrupt flag for IRQ handler -volatile uint8_t m_common_sw_irq_flag = 0; +static bool us_ticker_initialized = false; -__STATIC_INLINE void rtc_ovf_event_check(void) +/* us ticker is driven by 1MHz clock and counter length is 16 bits. */ +const ticker_info_t* us_ticker_get_info() { - if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) { - nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); - // Don't disable this event. It shall occur periodically. - - ++m_common_rtc_overflows; - } + static const ticker_info_t info = { + US_TICKER_FREQ, + US_TICKER_COUNTER_BITS + }; + return &info; } -#if defined(TARGET_MCU_NRF51822) -void common_rtc_irq_handler(void) -#else -void COMMON_RTC_IRQ_HANDLER(void) -#endif +void us_ticker_init(void) { - - rtc_ovf_event_check(); - - if ((m_common_sw_irq_flag & US_TICKER_SW_IRQ_MASK) || nrf_rtc_event_pending(COMMON_RTC_INSTANCE, US_TICKER_EVENT)) { - us_ticker_irq_handler(); - } - -#if DEVICE_LPTICKER - if (m_common_sw_irq_flag & LP_TICKER_SW_IRQ_MASK) { - m_common_sw_irq_flag &= ~LP_TICKER_SW_IRQ_MASK; - lp_ticker_irq_handler(); + if (us_ticker_initialized) { + nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0); + nrf_timer_int_disable(NRF_TIMER1, nrf_timer_compare_int_get(NRF_TIMER_CC_CHANNEL0)); + return; } - if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, LP_TICKER_EVENT)) { - lp_ticker_irq_handler(); - } -#endif -} - -// Function for fix errata 20: RTC Register values are invalid -__STATIC_INLINE void errata_20(void) -{ -#if defined(NRF52_ERRATA_20) - if (!softdevice_handler_is_enabled()) - { - NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; - NRF_CLOCK->TASKS_LFCLKSTART = 1; - - while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) - { - } - } - NRF_RTC1->TASKS_STOP = 0; -#endif -} + nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP); -void RTC1_IRQHandler(void); + nrf_timer_int_disable(NRF_TIMER1, nrf_timer_compare_int_get(NRF_TIMER_CC_CHANNEL0)); -void common_rtc_init(void) -{ - if (m_common_rtc_enabled) { - return; - } + /* Configure timer as follows: + * - timer mode, + * - timer width 16 bits, + * - timer freq 1 MHz. + */ + nrf_timer_mode_set(NRF_TIMER1, NRF_TIMER_MODE_TIMER); - errata_20(); + nrf_timer_frequency_set(NRF_TIMER1, NRF_TIMER_FREQ_1MHz); - NVIC_SetVector(RTC1_IRQn, (uint32_t)RTC1_IRQHandler); - - // RTC is driven by the low frequency (32.768 kHz) clock, a proper request - // must be made to have it running. - // Currently this clock is started in 'SystemInit' (see "system_nrf51.c" - // or "system_nrf52.c", respectively). + nrf_timer_bit_width_set(NRF_TIMER1, NRF_TIMER_BIT_WIDTH_16); - nrf_rtc_prescaler_set(COMMON_RTC_INSTANCE, 0); + nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, 0); - nrf_rtc_event_clear(COMMON_RTC_INSTANCE, US_TICKER_EVENT); -#if defined(TARGET_MCU_NRF51822) - nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT); -#endif -#if DEVICE_LPTICKER - nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT); -#endif - nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0); - // Interrupts on all related events are enabled permanently. Particular - // events will be enabled or disabled as needed (such approach is more - // energy efficient). - nrf_rtc_int_enable(COMMON_RTC_INSTANCE, -#if DEVICE_LPTICKER - LP_TICKER_INT_MASK | -#endif - US_TICKER_INT_MASK | - NRF_RTC_INT_OVERFLOW_MASK); + NVIC_SetVector(TIMER1_IRQn, (uint32_t)us_ticker_irq_handler); - // This event is enabled permanently, since overflow indications are needed - // continuously. - nrf_rtc_event_enable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - // All other relevant events are initially disabled. - nrf_rtc_event_disable(COMMON_RTC_INSTANCE, -#if defined(TARGET_MCU_NRF51822) - OS_TICK_INT_MASK | -#endif -#if DEVICE_LPTICKER - LP_TICKER_INT_MASK | -#endif - US_TICKER_INT_MASK); + nrf_drv_common_irq_enable(TIMER1_IRQn, - nrf_drv_common_irq_enable(nrf_drv_get_IRQn(COMMON_RTC_INSTANCE), #ifdef NRF51 APP_IRQ_PRIORITY_LOW #elif defined(NRF52) || defined(NRF52840_XXAA) @@ -169,167 +93,49 @@ void common_rtc_init(void) #endif ); - nrf_rtc_task_trigger(COMMON_RTC_INSTANCE, NRF_RTC_TASK_START); + nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START); - m_common_rtc_enabled = true; + us_ticker_initialized = true; } -__STATIC_INLINE void rtc_ovf_event_safe_check(void) -{ - core_util_critical_section_enter(); - - rtc_ovf_event_check(); - - core_util_critical_section_exit(); -} - - -uint32_t common_rtc_32bit_ticks_get(void) +uint32_t us_ticker_read() { - uint32_t ticks; - uint32_t prev_overflows; - - do { - prev_overflows = m_common_rtc_overflows; - - ticks = nrf_rtc_counter_get(COMMON_RTC_INSTANCE); - // The counter used for time measurements is less than 32 bit wide, - // so its value is complemented with the number of registered overflows - // of the counter. - ticks += (m_common_rtc_overflows << RTC_COUNTER_BITS); - - // Check in case that OVF occurred during execution of a RTC handler (apply if call was from RTC handler) - // m_common_rtc_overflows might been updated in this call. - rtc_ovf_event_safe_check(); - - // If call was made from a low priority level m_common_rtc_overflows might have been updated in RTC handler. - } while (m_common_rtc_overflows != prev_overflows); - - return ticks; -} + nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CAPTURE1); -uint64_t common_rtc_64bit_us_get(void) -{ - uint32_t ticks = common_rtc_32bit_ticks_get(); - // [ticks -> microseconds] - return ROUNDED_DIV(((uint64_t)ticks) * 1000000, RTC_INPUT_FREQ); + return nrf_timer_cc_read(NRF_TIMER1, NRF_TIMER_CC_CHANNEL1); } -void common_rtc_set_interrupt(uint32_t us_timestamp, uint32_t cc_channel, - uint32_t int_mask) +void us_ticker_set_interrupt(timestamp_t timestamp) { - // The internal counter is clocked with a frequency that cannot be easily - // multiplied to 1 MHz, therefore besides the translation of values - // (microsecond <-> ticks) a special care of overflows handling must be - // taken. Here the 32-bit timestamp value is complemented with information - // about current the system up time of (ticks + number of overflows of tick - // counter on upper bits, converted to microseconds), and such 64-bit value - // is then translated to counter ticks. Finally, the lower 24 bits of thus - // calculated value is written to the counter compare register to prepare - // the interrupt generation. - uint64_t current_time64 = common_rtc_64bit_us_get(); - // [add upper 32 bits from the current time to the timestamp value] - uint64_t timestamp64 = us_timestamp + - (current_time64 & ~(uint64_t)0xFFFFFFFF); - // [if the original timestamp value happens to be after the 32 bit counter - // of microsends overflows, correct the upper 32 bits accordingly] - if (us_timestamp < (uint32_t)(current_time64 & 0xFFFFFFFF)) { - timestamp64 += ((uint64_t)1 << 32); - } - // [microseconds -> ticks, always round the result up to avoid too early - // interrupt generation] - uint32_t compare_value = - (uint32_t)CEIL_DIV((timestamp64) * RTC_INPUT_FREQ, 1000000); + core_util_critical_section_enter(); + nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, timestamp & 0xFFFF); - core_util_critical_section_enter(); - // The COMPARE event occurs when the value in compare register is N and - // the counter value changes from N-1 to N. Therefore, the minimal safe - // difference between the compare value to be set and the current counter - // value is 2 ticks. This guarantees that the compare trigger is properly - // setup before the compare condition occurs. - uint32_t closest_safe_compare = common_rtc_32bit_ticks_get() + 2; - if ((int)(compare_value - closest_safe_compare) <= 0) { - compare_value = closest_safe_compare; + if (!nrf_timer_int_enable_check(NRF_TIMER1, nrf_timer_compare_int_get(NRF_TIMER_CC_CHANNEL0))) { + nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0); + nrf_timer_int_enable(NRF_TIMER1, nrf_timer_compare_int_get(NRF_TIMER_CC_CHANNEL0)); } - nrf_rtc_cc_set(COMMON_RTC_INSTANCE, cc_channel, RTC_WRAP(compare_value)); - nrf_rtc_event_enable(COMMON_RTC_INSTANCE, int_mask); - core_util_critical_section_exit(); } -//------------------------------------------------------------------------------ - - -void us_ticker_init(void) -{ - common_rtc_init(); -} - -void us_ticker_free(void) -{ - // A common counter is used for RTC, lp_ticker and us_ticker, so it can't be - // disabled here, but this does not cause any extra cost. -} - -uint32_t us_ticker_read() -{ - us_ticker_init(); - return (uint32_t)common_rtc_64bit_us_get(); -} - -void us_ticker_set_interrupt(timestamp_t timestamp) -{ - common_rtc_set_interrupt(timestamp, - US_TICKER_CC_CHANNEL, US_TICKER_INT_MASK); -} void us_ticker_fire_interrupt(void) { - core_util_critical_section_enter(); - m_common_sw_irq_flag |= US_TICKER_SW_IRQ_MASK; - NVIC_SetPendingIRQ(RTC1_IRQn); - core_util_critical_section_exit(); + NVIC_SetPendingIRQ(TIMER1_IRQn); } void us_ticker_disable_interrupt(void) { - nrf_rtc_event_disable(COMMON_RTC_INSTANCE, US_TICKER_INT_MASK); + nrf_timer_int_disable(NRF_TIMER1, nrf_timer_compare_int_get(NRF_TIMER_CC_CHANNEL0)); } void us_ticker_clear_interrupt(void) { - m_common_sw_irq_flag &= ~US_TICKER_SW_IRQ_MASK; - nrf_rtc_event_clear(COMMON_RTC_INSTANCE, US_TICKER_EVENT); + nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0); } - -// Since there is no SysTick on NRF51, the RTC1 channel 1 is used as an -// alternative source of RTOS ticks. -#if defined(TARGET_MCU_NRF51822) - -#include "mbed_toolchain.h" - - -#define MAX_RTC_COUNTER_VAL ((1uL << RTC_COUNTER_BITS) - 1) - -#ifndef RTC1_CONFIG_FREQUENCY - #define RTC1_CONFIG_FREQUENCY 32678 // [Hz] -#endif - - - -void COMMON_RTC_IRQ_HANDLER(void) -{ - if(!nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) { - common_rtc_irq_handler(); - } -} - -IRQn_Type mbed_get_m0_tick_irqn() +void us_ticker_free(void) { - return SWI3_IRQn; + // A common counter is used for RTC, lp_ticker and us_ticker, so it can't be + // disabled here, but this does not cause any extra cost. } - - -#endif // defined(TARGET_MCU_NRF51822) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.h b/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.h new file mode 100644 index 00000000000..f9bf80424c6 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.h @@ -0,0 +1,26 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef US_TICKER_H +#define US_TICKER_H + +/* TIMER0 is reserved for SoftDevice. We will use TIMER1 for us ticker + * which counter size is 16 bits. */ + +#define US_TICKER_COUNTER_BITS 16u +#define US_TICKER_FREQ 1000000 + +#endif // US_TICKER_H From d7001443a4f372351786a855a23e000322e02bd6 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Thu, 18 Jan 2018 07:59:36 +0100 Subject: [PATCH 2/9] Enable us/lp ticker support for NRF51_DK board --- targets/targets.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/targets/targets.json b/targets/targets.json index 93cf4c4c550..ec4edd635ff 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -3431,7 +3431,8 @@ "TARGET_MCU_NRF51822", "CMSIS_VECTAB_VIRTUAL", "CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\"", - "NO_SYSTICK" + "NO_SYSTICK", + "MBED_TICKLESS" ], "MERGE_BOOTLOADER": false, "extra_labels": ["NORDIC", "MCU_NRF51", "MCU_NRF51822_UNIFIED", "NRF5", "SDK11"], @@ -3476,8 +3477,8 @@ "NRF51_DK": { "supported_form_factors": ["ARDUINO"], "inherits": ["MCU_NRF51_32K_UNIFIED"], - "device_has": ["ANALOGIN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"], - "release_versions": ["2"], + "device_has": ["USTICKER", "LPTICKER", "ANALOGIN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"], + "release_versions": ["2", "5"], "device_name": "nRF51822_xxAA" }, "NRF51_DONGLE": { From bab5e60c8425b9a4d50129423acaafd9fd8fbe97 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Fri, 19 Jan 2018 14:48:10 +0100 Subject: [PATCH 3/9] Update lp ticker to be consistent with the new standards. Provide the following modifications for lp ticker driver: - According to NRF51_DK reference manual rtc interrupt cannot be controlled by rtc event. In the previous implementation interrupts were enabled permanently and specific interrupt was enabled/disabled by enabling/disabling the specific event. If event is enabled, then event signal is provided to Programmable Peripheral Interconnect (PPI). If interrupt is enabled, then interrupt signal is provided to Nested Vector Interrupt Controller (NVIC). Disable all events permanently. Enable lp ticker overflow interrupt permanently(needed for RTC), disable lp ticker capture/compare interrupt on init (lp_ticker_init) , enable lp ticker interrupt when lp ticker interrupt is set (lp_ticker_set_interrupt), disable lp ticker interrupt on disable request(lp_ticker_disable_interrupt). - Provide lp ticker data for higher level (freq: 32kHz / len: 24 bits), - Add the following features to init routine: disable lp ticker interrupt. - Make ticker driver to operate on ticks instead of us. - Simplify lp ticker read and set interrupt routines (upper layers handle conversion to us and interrupt scheduling). --- .../TARGET_NORDIC/TARGET_NRF5/common_rtc.c | 159 +++++++----------- .../TARGET_NORDIC/TARGET_NRF5/common_rtc.h | 6 +- targets/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c | 22 ++- 3 files changed, 80 insertions(+), 107 deletions(-) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c index 6fdd2c688a8..4915c56e182 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c @@ -47,6 +47,7 @@ #include "softdevice_handler.h" #endif + //------------------------------------------------------------------------------ // Common stuff used also by lp_ticker and rtc_api (see "common_rtc.h"). // @@ -54,12 +55,14 @@ bool m_common_rtc_enabled = false; uint32_t volatile m_common_rtc_overflows = 0; +bool volatile lp_ticker_interrupt_fire = false; __STATIC_INLINE void rtc_ovf_event_check(void) { if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) { nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); - // Don't disable this event. It shall occur periodically. + /* Don't disable this event. It shall occur periodically. + * It is needed for RTC. */ ++m_common_rtc_overflows; } @@ -74,14 +77,19 @@ void COMMON_RTC_IRQ_HANDLER(void) rtc_ovf_event_check(); #if DEVICE_LOWPOWERTIMER - if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, LP_TICKER_EVENT)) { + if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, LP_TICKER_EVENT) || + lp_ticker_interrupt_fire) { + + if (lp_ticker_interrupt_fire) { + lp_ticker_interrupt_fire = false; + } lp_ticker_irq_handler(); } #endif } -// Function for fix errata 20: RTC Register values are invalid +/* Function for fix errata 20: RTC Register values are invalid. */ __STATIC_INLINE void errata_20(void) { #if defined(NRF52_ERRATA_20) @@ -103,20 +111,28 @@ void RTC1_IRQHandler(void); void common_rtc_init(void) { if (m_common_rtc_enabled) { +#if DEVICE_LOWPOWERTIMER + nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT); + nrf_rtc_int_disable(COMMON_RTC_INSTANCE, LP_TICKER_INT_MASK); +#endif return; } errata_20(); + nrf_rtc_task_trigger(COMMON_RTC_INSTANCE, NRF_RTC_TASK_STOP); + NVIC_SetVector(RTC1_IRQn, (uint32_t)RTC1_IRQHandler); - // RTC is driven by the low frequency (32.768 kHz) clock, a proper request - // must be made to have it running. - // Currently this clock is started in 'SystemInit' (see "system_nrf51.c" - // or "system_nrf52.c", respectively). + /* RTC is driven by the low frequency (32.768 kHz) clock, a proper request + * must be made to have it running. + * Currently this clock is started in 'SystemInit' (see "system_nrf51.c" + * or "system_nrf52.c", respectively). + */ nrf_rtc_prescaler_set(COMMON_RTC_INSTANCE, 0); + /* Clear all RTC events. */ #if defined(TARGET_MCU_NRF51822) nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT); #endif @@ -125,27 +141,29 @@ void common_rtc_init(void) #endif nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); - // Interrupts on all related events are enabled permanently. Particular - // events will be enabled or disabled as needed (such approach is more - // energy efficient). - nrf_rtc_int_enable(COMMON_RTC_INSTANCE, -#if DEVICE_LOWPOWERTIMER - LP_TICKER_INT_MASK | -#endif - NRF_RTC_INT_OVERFLOW_MASK); + /* Disable all RTC events (According to NRF_51 Reference Manual + * RTC events can not be used to control RTC interrupts). + * IRQ signal to NVIC is provided if interrupt is enabled. + */ - // This event is enabled permanently, since overflow indications are needed - // continuously. - nrf_rtc_event_enable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - // All other relevant events are initially disabled. - nrf_rtc_event_disable(COMMON_RTC_INSTANCE, 0 | + nrf_rtc_event_disable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK #if defined(TARGET_MCU_NRF51822) - OS_TICK_INT_MASK | + | OS_TICK_INT_MASK #endif #if DEVICE_LOWPOWERTIMER - LP_TICKER_INT_MASK + | LP_TICKER_INT_MASK +#endif + ); + + /* This interrupt is enabled permanently, since overflow indications are needed + * continuously. + */ + nrf_rtc_int_enable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + + /* Disable LP ticker interrupt for now. */ +#if DEVICE_LOWPOWERTIMER + nrf_rtc_int_disable(COMMON_RTC_INSTANCE, LP_TICKER_INT_MASK); #endif - ); nrf_drv_common_irq_enable(nrf_drv_get_IRQn(COMMON_RTC_INSTANCE), #ifdef NRF51 @@ -160,93 +178,38 @@ void common_rtc_init(void) m_common_rtc_enabled = true; } -__STATIC_INLINE void rtc_ovf_event_safe_check(void) -{ - core_util_critical_section_enter(); - - rtc_ovf_event_check(); - - core_util_critical_section_exit(); -} - - -uint32_t common_rtc_32bit_ticks_get(void) +void common_rtc_set_interrupt(uint32_t ticks_count, uint32_t cc_channel, + uint32_t int_mask) { - uint32_t ticks; - uint32_t prev_overflows; - - do { - prev_overflows = m_common_rtc_overflows; - - ticks = nrf_rtc_counter_get(COMMON_RTC_INSTANCE); - // The counter used for time measurements is less than 32 bit wide, - // so its value is complemented with the number of registered overflows - // of the counter. - ticks += (m_common_rtc_overflows << RTC_COUNTER_BITS); - - // Check in case that OVF occurred during execution of a RTC handler (apply if call was from RTC handler) - // m_common_rtc_overflows might been updated in this call. - rtc_ovf_event_safe_check(); - - // If call was made from a low priority level m_common_rtc_overflows might have been updated in RTC handler. - } while (m_common_rtc_overflows != prev_overflows); + /* Set ticks value when interrupt should be fired. + * Interrupt scheduling is performed in upper layers. */ - return ticks; -} + core_util_critical_section_enter(); -uint64_t common_rtc_64bit_us_get(void) -{ - uint32_t ticks = common_rtc_32bit_ticks_get(); - // [ticks -> microseconds] - return ROUNDED_DIV(((uint64_t)ticks) * 1000000, RTC_INPUT_FREQ); -} + /* COMPARE occurs when a CC register is N and the COUNTER value transitions from N-1 to N. + * If the COUNTER is N, writing N+2 to a CC register is guaranteed to trigger a + * COMPARE event at N+2. + */ + const uint32_t now = nrf_rtc_counter_get(COMMON_RTC_INSTANCE); -void common_rtc_set_interrupt(uint32_t us_timestamp, uint32_t cc_channel, - uint32_t int_mask) -{ - // The internal counter is clocked with a frequency that cannot be easily - // multiplied to 1 MHz, therefore besides the translation of values - // (microsecond <-> ticks) a special care of overflows handling must be - // taken. Here the 32-bit timestamp value is complemented with information - // about current the system up time of (ticks + number of overflows of tick - // counter on upper bits, converted to microseconds), and such 64-bit value - // is then translated to counter ticks. Finally, the lower 24 bits of thus - // calculated value is written to the counter compare register to prepare - // the interrupt generation. - uint64_t current_time64 = common_rtc_64bit_us_get(); - // [add upper 32 bits from the current time to the timestamp value] - uint64_t timestamp64 = us_timestamp + - (current_time64 & ~(uint64_t)0xFFFFFFFF); - // [if the original timestamp value happens to be after the 32 bit counter - // of microsends overflows, correct the upper 32 bits accordingly] - if (us_timestamp < (uint32_t)(current_time64 & 0xFFFFFFFF)) { - timestamp64 += ((uint64_t)1 << 32); + if (now == ticks_count || + RTC_WRAP(now + 1) == ticks_count) { + ticks_count += 2; } - // [microseconds -> ticks, always round the result up to avoid too early - // interrupt generation] - uint32_t compare_value = - (uint32_t)CEIL_DIV((timestamp64) * RTC_INPUT_FREQ, 1000000); + nrf_rtc_cc_set(COMMON_RTC_INSTANCE, cc_channel, RTC_WRAP(ticks_count)); - core_util_critical_section_enter(); - // The COMPARE event occurs when the value in compare register is N and - // the counter value changes from N-1 to N. Therefore, the minimal safe - // difference between the compare value to be set and the current counter - // value is 2 ticks. This guarantees that the compare trigger is properly - // setup before the compare condition occurs. - uint32_t closest_safe_compare = common_rtc_32bit_ticks_get() + 2; - if ((int)(compare_value - closest_safe_compare) <= 0) { - compare_value = closest_safe_compare; + if (!nrf_rtc_int_is_enabled(COMMON_RTC_INSTANCE, int_mask)) { + nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT); + nrf_rtc_int_enable(COMMON_RTC_INSTANCE, int_mask); } - nrf_rtc_cc_set(COMMON_RTC_INSTANCE, cc_channel, RTC_WRAP(compare_value)); - nrf_rtc_event_enable(COMMON_RTC_INSTANCE, int_mask); - core_util_critical_section_exit(); } -// Since there is no SysTick on NRF51, the RTC1 channel 1 is used as an -// alternative source of RTOS ticks. +/* Since there is no SysTick on NRF51, the RTC1 channel 0 is used as an + * alternative source of RTOS ticks. + */ #if defined(TARGET_MCU_NRF51822) #include "mbed_toolchain.h" diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h index 6e5cefc20d3..dae27b263c4 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.h @@ -20,6 +20,7 @@ #include "nrf_rtc.h" #define RTC_COUNTER_BITS 24u +#define RTC_FREQ 32768u // Instance 0 is reserved for SoftDevice. // Instance 1 is used as a common one for lp_ticker and (in case @@ -29,9 +30,6 @@ #define OS_TICK_CC_CHANNEL 0 #define LP_TICKER_CC_CHANNEL 1 -#define US_TICKER_SW_IRQ_MASK 0x1 -#define LP_TICKER_SW_IRQ_MASK 0x2 - #define COMMON_RTC_EVENT_COMPARE(channel) \ CONCAT_2(NRF_RTC_EVENT_COMPARE_, channel) #define COMMON_RTC_INT_COMPARE_MASK(channel) \ @@ -44,7 +42,7 @@ extern bool m_common_rtc_enabled; extern uint32_t volatile m_common_rtc_overflows; -extern uint8_t volatile m_common_sw_irq_flag; +extern bool volatile lp_ticker_interrupt_fire; void common_rtc_init(void); uint32_t common_rtc_32bit_ticks_get(void); diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c b/targets/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c index a6d8bd0db17..9e197d8555e 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5/lp_ticker.c @@ -14,11 +14,20 @@ * limitations under the License. */ #include "lp_ticker_api.h" +#include "common_rtc.h" +#include "platform/mbed_critical.h" #if DEVICE_LPTICKER -#include "common_rtc.h" -#include "mbed_critical.h" +/* LP ticker is driven by 32kHz clock and counter length is 24 bits. */ +const ticker_info_t* lp_ticker_get_info() +{ + static const ticker_info_t info = { + RTC_FREQ, + RTC_COUNTER_BITS + }; + return &info; +} void lp_ticker_init(void) { @@ -33,7 +42,7 @@ void lp_ticker_free(void) uint32_t lp_ticker_read() { - return (uint32_t)common_rtc_64bit_us_get(); + return nrf_rtc_counter_get(COMMON_RTC_INSTANCE); } void lp_ticker_set_interrupt(timestamp_t timestamp) @@ -45,14 +54,17 @@ void lp_ticker_set_interrupt(timestamp_t timestamp) void lp_ticker_fire_interrupt(void) { core_util_critical_section_enter(); - m_common_sw_irq_flag |= LP_TICKER_SW_IRQ_MASK; + + lp_ticker_interrupt_fire = true; + NVIC_SetPendingIRQ(RTC1_IRQn); + core_util_critical_section_exit(); } void lp_ticker_disable_interrupt(void) { - nrf_rtc_event_disable(COMMON_RTC_INSTANCE, LP_TICKER_INT_MASK); + nrf_rtc_int_disable(COMMON_RTC_INSTANCE, LP_TICKER_INT_MASK); } void lp_ticker_clear_interrupt(void) From 098a30a27fb63dd3227ab2c1de9627b77651e006 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Tue, 6 Mar 2018 10:05:35 +0100 Subject: [PATCH 4/9] tests-mbed_drivers-timerevent: perform test only if USTICKER support is enabled. --- TESTS/mbed_drivers/timerevent/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TESTS/mbed_drivers/timerevent/main.cpp b/TESTS/mbed_drivers/timerevent/main.cpp index 5dee5786459..aeb155b2b84 100644 --- a/TESTS/mbed_drivers/timerevent/main.cpp +++ b/TESTS/mbed_drivers/timerevent/main.cpp @@ -23,6 +23,10 @@ using namespace utest::v1; +#if !DEVICE_USTICKER + #error [NOT_SUPPORTED] test not supported +#endif + #define TEST_DELAY_US 50000ULL class TestTimerEvent: public TimerEvent { From 21058a47761faecec9e14008512c5fac8472faf1 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Tue, 6 Mar 2018 11:21:35 +0100 Subject: [PATCH 5/9] tests-mbed_hal-lp_ticker: run sleep test only if SLEEP support is enabled. --- TESTS/mbed_hal/lp_ticker/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TESTS/mbed_hal/lp_ticker/main.cpp b/TESTS/mbed_hal/lp_ticker/main.cpp index 361a5808af6..dfea8facc8f 100644 --- a/TESTS/mbed_hal/lp_ticker/main.cpp +++ b/TESTS/mbed_hal/lp_ticker/main.cpp @@ -87,6 +87,7 @@ void lp_ticker_info_test() TEST_ASSERT(p_ticker_info->bits >= 12); } +#if DEVICE_SLEEP /* Test that the ticker continues operating in deep sleep mode. */ void lp_ticker_deepsleep_test() { @@ -115,6 +116,7 @@ void lp_ticker_deepsleep_test() TEST_ASSERT_EQUAL(1, intFlag); } +#endif /* Test that the ticker does not glitch backwards due to an incorrectly implemented ripple counter driver. */ void lp_ticker_glitch_test() @@ -141,7 +143,9 @@ utest::v1::status_t test_setup(const size_t number_of_cases) Case cases[] = { Case("lp ticker info test", lp_ticker_info_test), +#if DEVICE_SLEEP Case("lp ticker sleep test", lp_ticker_deepsleep_test), +#endif Case("lp ticker glitch test", lp_ticker_glitch_test) }; From 6c6e36fef52ee34ac06b9a76f31f491fb3002fa7 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Fri, 9 Mar 2018 11:40:54 +0100 Subject: [PATCH 6/9] tests-mbed_drivers-lp_ticker: Adapt tolerance for new Ticker driver Since this change concerns new ticker driver for NRF51_DK it goas on ticker feature branch. On master NRF51_DK Ticker driver uses 32kHz RTC for lp and us ticker. Test uses us ticker to measure lp ticker interrupt scheduling (LowPowerTicker.attach), but since the same hardware is used for both tickers the measurement error is constant. On this branch us ticker uses 1 MHz higher precision clock and the results are different - measurement error grows linearly and shows inaccuracy of the RTC. Test implements constant delta for measured time equal to 2000 us. Change delta so it depends on lp ticker tested timeout value: 500us for measurement inaccuracy + 5% tolerance for LowPowerTicker --- TESTS/mbed_drivers/lp_ticker/main.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/TESTS/mbed_drivers/lp_ticker/main.cpp b/TESTS/mbed_drivers/lp_ticker/main.cpp index 1d687cc28d1..7e706c89369 100644 --- a/TESTS/mbed_drivers/lp_ticker/main.cpp +++ b/TESTS/mbed_drivers/lp_ticker/main.cpp @@ -33,8 +33,9 @@ static const int test_timeout = 10; /* Due to poor accuracy of LowPowerTicker on many platforms there is no sense to tune tolerance value as it was in Ticker tests. - Tolerance value is set to 2000us to cover this diversity */ -#define TOLERANCE_US 2000 + Tolerance value is set to 500us for measurement inaccuracy + 5% tolerance + for LowPowerTicker. */ +#define TOLERANCE_US(DELAY) (500 + DELAY / 20) volatile uint32_t ticker_callback_flag; @@ -117,7 +118,7 @@ void test_multi_call_time(void) while(!ticker_callback_flag); time_diff = gtimer.read_us(); - TEST_ASSERT_UINT32_WITHIN(TOLERANCE_US, MULTI_TICKER_TIME_MS * 1000, time_diff); + TEST_ASSERT_UINT32_WITHIN(TOLERANCE_US(MULTI_TICKER_TIME_MS * 1000), MULTI_TICKER_TIME_MS * 1000, time_diff); } } @@ -167,7 +168,7 @@ void test_attach_time(void) ticker.detach(); const int time_diff = gtimer.read_us(); - TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US, DELAY_US, time_diff); + TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US(DELAY_US), DELAY_US, time_diff); } /** Test single callback time via attach_us @@ -189,7 +190,7 @@ void test_attach_us_time(void) ticker.detach(); const int time_diff = gtimer.read_us(); - TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US, DELAY_US, time_diff); + TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US(DELAY_US), DELAY_US, time_diff); } // Test cases From 467f6e2a1f2c29f8217606be1bad22212971fa93 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Fri, 9 Mar 2018 12:39:42 +0100 Subject: [PATCH 7/9] tests-mbed_drivers-timer: Adapt tolerance for new Ticker driver Since this change concerns new ticker driver for NRF51_DK it goas on ticker feature branch. On this branch us ticker is based on 1 MHz higher precision counter (on master is based on 32kHz RTC). As a result of using higher precision counter measurement error increased: :485::FAIL: Expected 1.060000 Was 1.070478 For delay 1060 ms we measured ~1070 ms so we have about ~1% measurement error which is not bed for this board. To make it work with 1MHz us ticker increase test tolerance. Define tolerance as follows: tolerance = 500us + 0.02 * delay. --- TESTS/mbed_drivers/timer/main.cpp | 115 ++++++++++++++---------------- 1 file changed, 52 insertions(+), 63 deletions(-) diff --git a/TESTS/mbed_drivers/timer/main.cpp b/TESTS/mbed_drivers/timer/main.cpp index 55eab966a85..51ac5f801c2 100644 --- a/TESTS/mbed_drivers/timer/main.cpp +++ b/TESTS/mbed_drivers/timer/main.cpp @@ -30,35 +30,24 @@ using namespace utest::v1; extern uint32_t SystemCoreClock; -/* Macro to define delta based on CPU clock frequency. - * - * Note that some extra time is counted by the timer. - * Additional time is caused by the function calls and - * additional operations performed by wait and - * stop functions before in fact timer is stopped. This may - * add additional time to the counted result. - * - * To take in to account this extra time we introduce DELTA - * value based on CPU clock (speed): - * DELTA = TOLERANCE_FACTOR / SystemCoreClock * US_FACTOR - * - * e.g. - * For K64F DELTA = (30000 / 120000000) * 1000000 = 250[us] - * For NUCLEO_F070RB DELTA = (30000 / 48000000) * 1000000 = 625[us] - * For NRF51_DK DELTA = (30000 / 16000000) * 1000000 = 1875[us] - */ #define US_PER_SEC 1000000 #define US_PER_MSEC 1000 -#define TOLERANCE_FACTOR 30000.0f -#define US_FACTOR 1000000.0f +#define MSEC_PER_SEC 1000 -static const int delta_sys_clk_us = ((int) (TOLERANCE_FACTOR / (float)SystemCoreClock * US_FACTOR)); - -/* When test performs time measurement using Timer in sequence, then measurement error accumulates - * in the successive attempts. */ - #define DELTA_US(i) (delta_sys_clk_us * i) - #define DELTA_S(i) ((float)delta_sys_clk_us * i / US_PER_SEC) - #define DELTA_MS(i) (1 + ( (i * delta_sys_clk_us) / US_PER_MSEC)) + /* + * Define tolerance as follows: + * tolerance = 500 us + 2% of measured time + * + * e.g. + * 1 ms delay: tolerance = 520 us + * 10 ms delay: tolerance = 700 us + * 100 ms delay: tolerance = 2500 us + * 1000 ms delay: tolerance = 20500 us + * + * */ +#define DELTA_US(delay_ms) (500 + delay_ms * US_PER_MSEC / 50) +#define DELTA_MS(delay_ms) (1 + (delay_ms * US_PER_MSEC / 50 / US_PER_MSEC)) +#define DELTA_S(delay_ms) (0.000500f + (((float)delay_ms) / MSEC_PER_SEC / 50)) #define TICKER_FREQ_1MHZ 1000000 #define TICKER_BITS 32 @@ -417,10 +406,10 @@ void test_timer_time_accumulation_os_ticker() p_timer->stop(); /* Check results - totally 10 ms have elapsed. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(1), 0.010f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(1), 10, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(1), 10000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(1), 10000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(10), 0.010f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(10), 10, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(10), 10000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(10), 10000, p_timer->read_high_resolution_us()); /* Wait 50 ms - this is done to show that time elapsed when * the timer is stopped does not have influence on the @@ -439,10 +428,10 @@ void test_timer_time_accumulation_os_ticker() p_timer->stop(); /* Check results - totally 30 ms have elapsed. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(2), 0.030f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(2), 30, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(2), 30000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(2), 30000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(30), 0.030f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(30), 30, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(30), 30000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(30), 30000, p_timer->read_high_resolution_us()); /* Wait 50 ms - this is done to show that time elapsed when * the timer is stopped does not have influence on the @@ -460,10 +449,10 @@ void test_timer_time_accumulation_os_ticker() p_timer->stop(); /* Check results - totally 60 ms have elapsed. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(3), 0.060f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(3), 60, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(3), 60000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(3), 60000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(60), 0.060f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(60), 60, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(60), 60000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(60), 60000, p_timer->read_high_resolution_us()); /* Wait 50 ms - this is done to show that time elapsed when * the timer is stopped does not have influence on the @@ -482,10 +471,10 @@ void test_timer_time_accumulation_os_ticker() p_timer->stop(); /* Check results - totally 1060 ms have elapsed. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(4), 1.060f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(4), 1060, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(4), 1060000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(4), 1060000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(1060), 1.060f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(1060), 1060, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(1060), 1060000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(1060), 1060000, p_timer->read_high_resolution_us()); } /* This test verifies if reset() function resets the timer @@ -511,10 +500,10 @@ void test_timer_reset_os_ticker() p_timer->stop(); /* Check results - totally 10 ms elapsed. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(1), 0.010f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(1), 10, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(1), 10000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(1), 10000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(10), 0.010f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(10), 10, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(10), 10000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(10), 10000, p_timer->read_high_resolution_us()); /* Reset the timer - previous measured time should be lost now. */ p_timer->reset(); @@ -529,10 +518,10 @@ void test_timer_reset_os_ticker() p_timer->stop(); /* Check results - 20 ms elapsed since the reset. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(1), 0.020f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(1), 20, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(1), 20000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(1), 20000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(20), 0.020f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(20), 20, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(20), 20000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(20), 20000, p_timer->read_high_resolution_us()); } /* This test verifies if reset() function resets the timer @@ -613,10 +602,10 @@ void test_timer_start_started_timer_os_ticker() p_timer->stop(); /* Check results - 30 ms have elapsed since the first start. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(2), 0.030f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(2), 30, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(2), 30000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(2), 30000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(30), 0.030f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(30), 30, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(30), 30000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(30), 30000, p_timer->read_high_resolution_us()); } /* This test verifies if calling start() for already @@ -650,10 +639,10 @@ void test_timer_start_started_timer_user_ticker() p_timer->stop(); /* Check results - 30 ms have elapsed since the first start. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(2), 0.030f, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(2), 30, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(2), 30000, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(2), 30000, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(30), 0.030f, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(30), 30, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(30), 30000, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(30), 30000, p_timer->read_high_resolution_us()); } /* This test verifies Timer float operator. @@ -677,7 +666,7 @@ void test_timer_float_operator_os_ticker() p_timer->stop(); /* Check result - 10 ms elapsed. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(1), 0.010f, (float)(*p_timer)); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(10), 0.010f, (float)(*p_timer)); } /* This test verifies Timer float operator. @@ -732,10 +721,10 @@ void test_timer_time_measurement() p_timer->stop(); /* Check results. */ - TEST_ASSERT_FLOAT_WITHIN(DELTA_S(1), (float)wait_val_us / 1000000, p_timer->read()); - TEST_ASSERT_INT32_WITHIN(DELTA_MS(1), wait_val_us / 1000, p_timer->read_ms()); - TEST_ASSERT_INT32_WITHIN(DELTA_US(1), wait_val_us, p_timer->read_us()); - TEST_ASSERT_UINT64_WITHIN(DELTA_US(1), wait_val_us, p_timer->read_high_resolution_us()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_S(wait_val_us / US_PER_MSEC), (float)wait_val_us / US_PER_SEC, p_timer->read()); + TEST_ASSERT_INT32_WITHIN(DELTA_MS(wait_val_us / US_PER_MSEC), wait_val_us / US_PER_MSEC, p_timer->read_ms()); + TEST_ASSERT_INT32_WITHIN(DELTA_US(wait_val_us / US_PER_MSEC), wait_val_us, p_timer->read_us()); + TEST_ASSERT_UINT64_WITHIN(DELTA_US(wait_val_us / US_PER_MSEC), wait_val_us, p_timer->read_high_resolution_us()); } utest::v1::status_t test_setup(const size_t number_of_cases) { From b832022f71a2628b456b4648c67ba2f9a7f53ea8 Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Wed, 28 Mar 2018 10:26:59 +0200 Subject: [PATCH 8/9] Rename DEVICE_LOWPOWERTIMER to DEVICE_LPTICKER. --- targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c index 4915c56e182..efeea8b608b 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5/common_rtc.c @@ -76,7 +76,7 @@ void COMMON_RTC_IRQ_HANDLER(void) { rtc_ovf_event_check(); -#if DEVICE_LOWPOWERTIMER +#if DEVICE_LPTICKER if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, LP_TICKER_EVENT) || lp_ticker_interrupt_fire) { @@ -111,7 +111,7 @@ void RTC1_IRQHandler(void); void common_rtc_init(void) { if (m_common_rtc_enabled) { -#if DEVICE_LOWPOWERTIMER +#if DEVICE_LPTICKER nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT); nrf_rtc_int_disable(COMMON_RTC_INSTANCE, LP_TICKER_INT_MASK); #endif @@ -136,7 +136,7 @@ void common_rtc_init(void) #if defined(TARGET_MCU_NRF51822) nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT); #endif -#if DEVICE_LOWPOWERTIMER +#if DEVICE_LPTICKER nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT); #endif nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); @@ -150,7 +150,7 @@ void common_rtc_init(void) #if defined(TARGET_MCU_NRF51822) | OS_TICK_INT_MASK #endif -#if DEVICE_LOWPOWERTIMER +#if DEVICE_LPTICKER | LP_TICKER_INT_MASK #endif ); @@ -161,7 +161,7 @@ void common_rtc_init(void) nrf_rtc_int_enable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); /* Disable LP ticker interrupt for now. */ -#if DEVICE_LOWPOWERTIMER +#if DEVICE_LPTICKER nrf_rtc_int_disable(COMMON_RTC_INSTANCE, LP_TICKER_INT_MASK); #endif From de4bcba7ecf549a151e2057d01eb37400210e93f Mon Sep 17 00:00:00 2001 From: Przemyslaw Stekiel Date: Mon, 23 Apr 2018 10:22:24 +0200 Subject: [PATCH 9/9] tests-mbed_hal-common_tickers: increase overflow protection limit For NR51_DK US_TICKER_OV_LIMIT needs to be increased since if test is run few times in row sometimes fails. This is because NR51_DK is a slow board (16 MHz) with fast and short us ticker counter 1 MHz/16 bits. --- TESTS/mbed_hal/common_tickers/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TESTS/mbed_hal/common_tickers/main.cpp b/TESTS/mbed_hal/common_tickers/main.cpp index 4724c1ed6e6..fd0498381d9 100644 --- a/TESTS/mbed_hal/common_tickers/main.cpp +++ b/TESTS/mbed_hal/common_tickers/main.cpp @@ -40,7 +40,7 @@ #define NUM_OF_CYCLES 100000 -#define US_TICKER_OV_LIMIT 20000 +#define US_TICKER_OV_LIMIT 35000 #define LP_TICKER_OV_LIMIT 4000 using namespace utest::v1;