diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e20e8aab7a..e1ea1ba67e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -208,6 +208,7 @@ config ARCH_CHIP_MOXART config ARCH_CHIP_NRF52 bool "Nordic NRF52" select ARCH_CORTEXM4 + select ARCH_HAVE_TICKLESS #select ARCH_HAVE_MPU #select ARM_HAVE_MPU_UNIFIED select ARCH_HAVE_FPU diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig index 6640ebf03d..8c07cf1fc2 100644 --- a/arch/arm/src/nrf52/Kconfig +++ b/arch/arm/src/nrf52/Kconfig @@ -291,6 +291,57 @@ endif # NRF52_USE_LFCLK endmenu # Clock Configuration +menu "System Timer" + +config NRF52_SYSTIMER + bool + default y + +choice + prompt "System Timer Source" + default NRF52_SYSTIMER_SYSTICK + ---help--- + Choose which hardware resource will drive NuttX + system time + +config NRF52_SYSTIMER_SYSTICK + bool "SysTick" + select TIMER_ARCH + select TIMER + select ARMV7M_SYSTICK + ---help--- + Use ARM SysTick. It can be used for tickless and + non-tickless mode. + + NOTE: nRF52 implementation of WFE/WFI involves is + incompatible with SysTick. This means that if + you choose this option, WFE/WFI will not be used + in idle loop. + +config NRF52_SYSTIMER_RTC + bool "RTC" + select NRF52_RTC + select SCHED_TICKLESS + select SCHED_TICKLESS_ALARM + select NRF52_USE_LFCLK + ---help--- + Use RTC timer in tickless mode. + +endchoice + +if NRF52_SYSTIMER_RTC + +config NRF52_SYSTIMER_RTC_INSTANCE + int "RTC timer instance" + default 0 + range 0 2 + ---help--- + Which RTC instance to use to drive the system timer + +endif + +endmenu # System Timer + config NRF52_FLASH_PREFETCH bool "Enable FLASH Pre-fetch" default y diff --git a/arch/arm/src/nrf52/Make.defs b/arch/arm/src/nrf52/Make.defs index 306d6f8c2b..ffce6a9df3 100644 --- a/arch/arm/src/nrf52/Make.defs +++ b/arch/arm/src/nrf52/Make.defs @@ -45,12 +45,20 @@ endif CMN_CSRCS = arm_assert.c arm_blocktask.c arm_copyfullstate.c CMN_CSRCS += arm_createstack.c arm_doirq.c arm_exit.c arm_hardfault.c CMN_CSRCS += arm_initialize.c arm_initialstate.c arm_interruptcontext.c -CMN_CSRCS += arm_memfault.c arm_mdelay.c arm_modifyreg8.c arm_modifyreg16.c +CMN_CSRCS += arm_memfault.c arm_modifyreg8.c arm_modifyreg16.c CMN_CSRCS += arm_modifyreg32.c arm_releasepending.c arm_releasestack.c CMN_CSRCS += arm_reprioritizertr.c arm_schedulesigaction.c arm_sigdeliver.c CMN_CSRCS += arm_stackframe.c arm_svcall.c arm_trigger_irq.c arm_udelay.c CMN_CSRCS += arm_unblocktask.c arm_usestack.c arm_vfork.c arm_systemreset.c +ifeq ($(CONFIG_NRF52_SYSTIMER_SYSTICK),y) +CMN_CSRCS += arm_systick.c nrf52_systick.c +else +ifeq ($(CONFIG_NRF52_SYSTIMER_RTC),y) +CMN_CSRCS += nrf52_tickless_rtc.c arm_mdelay.c +endif +endif + ifeq ($(CONFIG_ARMV7M_LAZYFPU),y) CMN_ASRCS += arm_lazyexception.S else @@ -85,12 +93,6 @@ ifeq ($(CONFIG_ARCH_CHIP_NRF52832),y) CHIP_CSRCS += nrf52832_errdata.c endif -ifneq ($(CONFIG_SCHED_TICKLESS),y) -CHIP_CSRCS += nrf52_timerisr.c -else -CHIP_CSRCS += nrf52_tickless.c -endif - ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += nrf52_userspace.c nrf52_mpuinit.c endif diff --git a/arch/arm/src/nrf52/nrf52_idle.c b/arch/arm/src/nrf52/nrf52_idle.c index 5d56678eef..4dc58df293 100644 --- a/arch/arm/src/nrf52/nrf52_idle.c +++ b/arch/arm/src/nrf52/nrf52_idle.c @@ -178,11 +178,11 @@ void up_idle(void) /* Sleep until an interrupt occurs to save power * - * REVISIT: The SysTick's clock will only tick when the CPU is + * The SysTick's clock will only tick when the CPU is * running (not in WFE/WFI) or when the system is in debug interface mode. */ -#if 0 +#ifdef CONFIG_NRF52_SYSTIMER_RTC BEGIN_IDLE(); asm("WFI"); END_IDLE(); diff --git a/arch/arm/src/nrf52/nrf52_timerisr.c b/arch/arm/src/nrf52/nrf52_systick.c similarity index 55% rename from arch/arm/src/nrf52/nrf52_timerisr.c rename to arch/arm/src/nrf52/nrf52_systick.c index 07f652fa8a..b0410d9120 100644 --- a/arch/arm/src/nrf52/nrf52_timerisr.c +++ b/arch/arm/src/nrf52/nrf52_systick.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/nrf52/nrf52_timerisr.c + * arch/arm/src/nrf52/nrf52_systick.c * * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -46,68 +46,17 @@ #include #include -#include "nvic.h" -#include "clock/clock.h" -#include "arm_internal.h" -#include "arm_arch.h" +#include +#include "systick.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* The SysTick clock may be clocked internally either by the by the system - * clock (CLKSOURCE==1) or by the SysTick function clock (CLKSOURCE==0). - * The SysTick Function clock is equal to: - * - * Fsystick = Fmainclk / SYSTICKCLKDIV - * - * Both the divider value (BOARD_SYSTICKCLKDIV) and the resulting SysTick - * function clock frequency (Fsystick, BOARD_SYSTICK_CLOCK) - * - * The desired timer interrupt frequency is provided by the definition - * CLK_TCK (see include/time.h). CLK_TCK defines the desired number of - * system clock ticks per second. That value is a user configurable setting - * that defaults to 100 (100 ticks per second = 10 MS interval). - * - * reload = (Fsystick / CLK_TICK) - 1 - * - * Tips for selecting BOARD_SYSTICKCLKDIV: The resulting reload value - * should be as large as possible, but must be less than 2^24: - * - * SYSTICKDIV > Fmainclk / CLK_TCK / 2^24 - */ - -#define SYSTICK_RELOAD ((BOARD_SYSTICK_CLOCK / CLK_TCK) - 1) - -/* The size of the reload field is 24 bits. Verify that the reload value - * will fit in the reload register. - */ - -#if SYSTICK_RELOAD > 0x00ffffff -# error SYSTICK_RELOAD exceeds the range of the RELOAD register -#endif - /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Function: nrf52_timerisr - * - * Description: - * The timer ISR will perform a variety of services for various portions - * of the systems. - * - ****************************************************************************/ - -static int nrf52_timerisr(int irq, uint32_t *regs, void *arg) -{ - /* Process timer interrupt */ - - nxsched_process_timer(); - return 0; -} - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -123,26 +72,7 @@ static int nrf52_timerisr(int irq, uint32_t *regs, void *arg) void up_timer_initialize(void) { - uint32_t regval; + /* Use SysTick to drive system timer */ - regval = getreg32(NVIC_SYSTICK_CTRL); - regval &= ~NVIC_SYSTICK_CTRL_CLKSOURCE; - putreg32(regval, NVIC_SYSTICK_CTRL); - - /* Configure SysTick to interrupt at the requested rate */ - - putreg32(SYSTICK_RELOAD, NVIC_SYSTICK_RELOAD); - - /* Attach the timer interrupt vector */ - - irq_attach(NRF52_IRQ_SYSTICK, (xcpt_t)nrf52_timerisr, NULL); - - /* Enable SysTick interrupts */ - - putreg32((NVIC_SYSTICK_CTRL_CLKSOURCE | NVIC_SYSTICK_CTRL_TICKINT | - NVIC_SYSTICK_CTRL_ENABLE), NVIC_SYSTICK_CTRL); - - /* And enable the timer interrupt */ - - up_enable_irq(NRF52_IRQ_SYSTICK); + up_timer_set_lowerhalf(systick_initialize(true, BOARD_SYSTICK_CLOCK, -1)); } diff --git a/arch/arm/src/nrf52/nrf52_tickless_rtc.c b/arch/arm/src/nrf52/nrf52_tickless_rtc.c new file mode 100644 index 0000000000..7d8a5dd035 --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_tickless_rtc.c @@ -0,0 +1,330 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_tickless_rtc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "arm_arch.h" + +#include "hardware/nrf52_rtc.h" +#include "nrf52_rtc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Check corresponding RTC support */ + +#if (CONFIG_NRF52_SYSTIMER_RTC_INSTANCE == 0) && !defined(CONFIG_NRF52_RTC0) +# error "Support for RTC0 is not enabled" +#elif (CONFIG_NRF52_SYSTIMER_RTC_INSTANCE == 1) && !defined(CONFIG_NRF52_RTC1) +# error "Support for RTC1 is not enabled" +#elif (CONFIG_NRF52_SYSTIMER_RTC_INSTANCE == 2) && !defined(CONFIG_NRF52_RTC2) +# error "Support for RTC2 is not enabled" +#endif + +/* TIMER configuration */ + +#if (CONFIG_NRF52_SYSTIMER_RTC_INSTANCE == 0) +# define NRF52_RTC_BASE NRF52_RTC0_BASE +#elif (CONFIG_NRF52_SYSTIMER_RTC_INSTANCE == 1) +# define NRF52_RTC_BASE NRF52_RTC1_BASE +#elif (CONFIG_NRF52_SYSTIMER_RTC_INSTANCE == 2) +# define NRF52_RTC_BASE NRF52_RTC2_BASE +#endif + +#define NRF52_RTC_PERIOD (512) +#define NRF52_RTC_MAX (0x00ffffff) +#define NRF52_RTC_MAX_TIME (NRF52_RTC_MAX * 31) + +/* Convert uS to timer count for f = 32768Hz, using more precision + * when possible: + * (1 / 32768) s ~ 30.51 uS ~ 31 uS + * 512 * (1 / 32768) s = 0.015625 s = 15625 uS + * So, instead of always dividing by 31, if t * 512 < (2**32 - 1), we can do: + * (t * 512) / 15625 ~ t / 30.51 + */ + +#define USEC_TO_COUNTER(t) (t > 0x7fffff ? (t / 31) : ((t * 512) / 15625)) + +/* To convert from counter to uS we split the counter into one second worth + * of counts (32768) and a fractional part we can safely multiply first + * by (USEC_PER_SEC/8) and still be within 32 bit value + */ + +#define COUNTER_TO_USEC(c) ((c / 32768) * USEC_PER_SEC) + \ + (((c % 32768) * (USEC_PER_SEC / 8)) / (32768 / 8)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_tickless_dev_s +{ + FAR struct nrf52_rtc_dev_s *rtc; /* nrf52 RTC driver */ + uint32_t periods; /* how many times the timer overflowed */ + bool alarm_set; /* is the alarm set? */ + struct timespec alarm; /* absolute time of alarm */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rtc_handler(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct nrf52_tickless_dev_s g_tickless_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void rtc_counter_to_ts(uint32_t counter, struct timespec *now) +{ + uint32_t usec; + + usec = COUNTER_TO_USEC(counter); + now->tv_sec = usec / USEC_PER_SEC; + now->tv_nsec = (usec % USEC_PER_SEC) * NSEC_PER_USEC; + + now->tv_sec += g_tickless_dev.periods * NRF52_RTC_PERIOD; +} + +static void rtc_prepare_alarm(void) +{ + struct timespec now; + struct timespec delta; + uint32_t usec; + uint32_t counter; + uint32_t target_counter; + + /* get current counter and absolute time */ + + NRF52_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); + rtc_counter_to_ts(counter, &now); + + /* obtain relative time to alarm */ + + clock_timespec_subtract(&g_tickless_dev.alarm, &now, &delta); + usec = delta.tv_sec * USEC_PER_SEC + delta.tv_nsec / NSEC_PER_USEC; + + /* if the alarm is to expire within one RTC period, we can set the CC */ + + if (usec < NRF52_RTC_PERIOD * USEC_PER_SEC) + { + /* usec is the time w.r.t. now, so we compute the counter compare value + * from current counter. as this may be "behind" the counter, we wrap + * around one full timer period + */ + + target_counter = USEC_TO_COUNTER(usec); + + if (target_counter < 2) + { + /* ensure counter fires, from nRF52832_PS_v1.4 (p. 245): + * "If the COUNTER is N, writing N+2 to a CC register is + * guaranteed to trigger a COMPARE event at N+2." + */ + + target_counter = 2; + } + + target_counter = (counter + target_counter) % (NRF52_RTC_MAX + 1); + + NRF52_RTC_SETCC(g_tickless_dev.rtc, NRF52_RTC_CC0, target_counter); + NRF52_RTC_ENABLEINT(g_tickless_dev.rtc, NRF52_RTC_EVT_COMPARE0); + } +} + +/**************************************************************************** + * Name: rtc_handler + ****************************************************************************/ + +static int rtc_handler(int irq, void *context, void *arg) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + /* if the timer wrapped-around */ + + if (getreg32(NRF52_RTC_BASE + NRF52_RTC_EVENTS_OVRFLW_OFFSET)) + { + /* ack interrupt */ + + NRF52_RTC_ACKINT(g_tickless_dev.rtc, NRF52_RTC_EVT_OVRFLW); + + /* count one more period */ + + g_tickless_dev.periods++; + + /* check if the currently set alarm is to fire in this new period */ + + if (g_tickless_dev.alarm_set) + { + rtc_prepare_alarm(); + } + } + + /* if the compare event fired */ + + if (getreg32(NRF52_RTC_BASE + NRF52_RTC_EVENTS_COMPARE_OFFSET(0))) + { + struct timespec now; + + /* cancel alarm and get current time */ + + up_alarm_cancel(&now); + + /* let scheduler now of alarm firing */ + + nxsched_alarm_expiration(&now); + } + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_alarm_cancel + ****************************************************************************/ + +int up_alarm_cancel(FAR struct timespec *ts) +{ + if (g_tickless_dev.alarm_set) + { + uint32_t counter; + irqstate_t flags; + + flags = enter_critical_section(); + + NRF52_RTC_DISABLEINT(g_tickless_dev.rtc, NRF52_RTC_EVT_COMPARE0); + NRF52_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); + rtc_counter_to_ts(counter, ts); + + NRF52_RTC_ACKINT(g_tickless_dev.rtc, NRF52_RTC_EVT_COMPARE0); + g_tickless_dev.alarm_set = false; + + leave_critical_section(flags); + } + + return OK; +} + +/**************************************************************************** + * Name: up_alarm_start + ****************************************************************************/ + +int up_alarm_start(FAR const struct timespec *ts) +{ + irqstate_t flags; + flags = enter_critical_section(); + + /* remember the alarm time */ + + g_tickless_dev.alarm_set = true; + g_tickless_dev.alarm = *ts; + + rtc_prepare_alarm(); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_timer_gettime + ****************************************************************************/ + +int up_timer_gettime(FAR struct timespec *ts) +{ + uint32_t counter; + irqstate_t flags; + + flags = enter_critical_section(); + + NRF52_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); + rtc_counter_to_ts(counter, ts); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_timer_initialize + ****************************************************************************/ + +void up_timer_initialize(void) +{ + struct timespec ts; + + g_tickless_dev.rtc = nrf52_rtc_init(CONFIG_NRF52_SYSTIMER_RTC_INSTANCE); + g_tickless_dev.periods = 0; + g_tickless_dev.alarm_set = false; + + /* Ensure we have support for the selected RTC instance */ + + ASSERT(g_tickless_dev.rtc); + + /* Configure prescaler */ + + NRF52_RTC_SETPRE(g_tickless_dev.rtc, 0); + + /* Configure ISR */ + + NRF52_RTC_SETISR(g_tickless_dev.rtc, rtc_handler, NULL); + + /* Enable overflow interrupt */ + + NRF52_RTC_ENABLEINT(g_tickless_dev.rtc, NRF52_RTC_EVT_OVRFLW); + + /* Start counting */ + + NRF52_RTC_CLEAR(g_tickless_dev.rtc); + NRF52_RTC_ACKINT(g_tickless_dev.rtc, NRF52_RTC_EVT_OVRFLW); + NRF52_RTC_ACKINT(g_tickless_dev.rtc, NRF52_RTC_EVT_COMPARE0); + NRF52_RTC_START(g_tickless_dev.rtc); + + /* kick off alarm scheduling */ + + ts.tv_sec = ts.tv_nsec = 0; + nxsched_alarm_expiration(&ts); +}