You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/04/21 17:08:11 UTC
[nuttx] 01/02: arch/arm64:Suppot tickless mode
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit ab0b3336c450ef6ed06c94fa544a84c6cfcd133e
Author: hujun5 <hu...@xiaomi.com>
AuthorDate: Fri Mar 17 15:49:43 2023 +0800
arch/arm64:Suppot tickless mode
1 Similar to Linux and zephyr, all implementations are in arm64_arch_timer.c
2 Arm64 tickless is turned off by default. If it needs to be turned on, you need to configure the switch CONFIG_SCHED_TICKLESS ON
3 The implementation strategy for tick/tickless is to use the timer inside the CPU and implement the timer driver based on the ARCH_TIMER framework.
4 We implemented tick_* Callback functions to adapt to the driven interface to avoid time format conversion overhead
5 In arm64_tick_cancel func,The remaining time that is not used, so this value can be ignored without reading the corresponding register to obtain the remaining cycles
6 Currently, tick/tickless can takes effect in SMP and non SMP mode, ostest can pass.
Signed-off-by: hujun5 <hu...@xiaomi.com>
---
arch/Kconfig | 2 +
arch/arm64/src/common/arm64_arch_timer.c | 344 ++++++++++++++++++++++++-------
arch/arm64/src/common/arm64_arch_timer.h | 6 +-
arch/arm64/src/common/arm64_cpustart.c | 2 +
4 files changed, 272 insertions(+), 82 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index 46311b93e1..122c7fa394 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -24,6 +24,7 @@ config ARCH_ARM
config ARCH_ARM64
bool "ARM64"
+ select ALARM_ARCH
select ARCH_HAVE_BACKTRACE
select ARCH_HAVE_INTERRUPTSTACK
select ARCH_HAVE_VFORK
@@ -33,6 +34,7 @@ config ARCH_ARM64
select ARCH_HAVE_SYSCALL_HOOKS
select ARCH_HAVE_RDWR_MEM_CPU_RUN
select ARCH_HAVE_THREAD_LOCAL
+ select ONESHOT
---help---
The ARM64 architectures
diff --git a/arch/arm64/src/common/arm64_arch_timer.c b/arch/arm64/src/common/arm64_arch_timer.c
index c2e860aa41..06489a3070 100644
--- a/arch/arm64/src/common/arm64_arch_timer.c
+++ b/arch/arm64/src/common/arm64_arch_timer.c
@@ -25,11 +25,13 @@
#include <nuttx/config.h>
#include <debug.h>
#include <assert.h>
+#include <stdio.h>
#include <nuttx/arch.h>
#include <arch/irq.h>
#include <arch/chip/chip.h>
#include <nuttx/spinlock.h>
+#include <nuttx/timers/arch_alarm.h>
#include "arm64_arch.h"
#include "arm64_internal.h"
@@ -37,17 +39,25 @@
#include "arm64_arch_timer.h"
/****************************************************************************
- * Pre-processor Definitions
+ * Private Types
****************************************************************************/
-#define MIN_DELAY (1000)
-/****************************************************************************
- * Private Data
- ****************************************************************************/
+struct arm64_oneshot_lowerhalf_s
+{
+ /* This is the part of the lower half driver that is visible to the upper-
+ * half client of the driver. This must be the first thing in this
+ * structure so that pointers to struct oneshot_lowerhalf_s are cast
+ * compatible to struct arm64_oneshot_lowerhalf_s and vice versa.
+ */
+
+ struct oneshot_lowerhalf_s lh; /* Common lower-half driver fields */
-static uint64_t last_cycle;
-static uint64_t cycle_per_tick;
-static uint32_t arch_timer_rate;
+ /* Private lower half data follows */
+
+ void *arg; /* Argument that is passed to the handler */
+ uint64_t cycle_per_tick; /* cycle per tick */
+ oneshot_callback_t callback; /* Internal handler that receives callback */
+};
/****************************************************************************
* Private Functions
@@ -58,6 +68,11 @@ static inline void arm64_arch_timer_set_compare(uint64_t value)
write_sysreg(value, cntv_cval_el0);
}
+static inline uint64_t arm64_arch_timer_get_compare(void)
+{
+ return read_sysreg(cntv_cval_el0);
+}
+
static inline void arm64_arch_timer_enable(bool enable)
{
uint64_t value;
@@ -99,133 +114,308 @@ static inline uint64_t arm64_arch_timer_count(void)
return read_sysreg(cntvct_el0);
}
-static inline uint32_t arm64_arch_timer_get_cntfrq(void)
+static inline uint64_t arm64_arch_timer_get_cntfrq(void)
{
return read_sysreg(cntfrq_el0);
}
-#ifdef CONFIG_SCHED_TICKLESS
+/****************************************************************************
+ * Name: arm64_arch_timer_compare_isr
+ *
+ * Description:
+ * Common timer interrupt callback. When any oneshot timer interrupt
+ * expires, this function will be called. It will forward the call to
+ * the next level up.
+ *
+ * Input Parameters:
+ * oneshot - The state associated with the expired timer
+ *
+ * Returned Value:
+ * Always returns OK
+ *
+ ****************************************************************************/
+
static int arm64_arch_timer_compare_isr(int irq, void *regs, void *arg)
{
- irqstate_t flags;
- uint64_t curr_cycle;
- uint32_t delta_ticks;
+ struct arm64_oneshot_lowerhalf_s *priv =
+ (struct arm64_oneshot_lowerhalf_s *)arg;
- UNUSED(regs);
- UNUSED(arg);
+ arm64_arch_timer_set_irq_mask(true);
- flags = spin_lock_irqsave(&g_arch_timer_lock);
+ if (priv->callback)
+ {
+ /* Then perform the callback */
- curr_cycle = arm64_arch_timer_count();
- delta_ticks = (uint32_t)((curr_cycle - last_cycle) / cycle_per_tick);
+ priv->callback(&priv->lh, priv->arg);
+ }
- last_cycle += delta_ticks * cycle_per_tick;
+ return OK;
+}
- arm_arch_timer_set_irq_mask(true);
+/****************************************************************************
+ * Name: arm64_tick_max_delay
+ *
+ * Description:
+ * Determine the maximum delay of the one-shot timer (in microseconds)
+ *
+ * Input Parameters:
+ * lower An instance of the lower-half oneshot state structure. This
+ * structure must have been previously initialized via a call to
+ * oneshot_initialize();
+ * ticks The location in which to return the maximum delay.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; a negated errno value is returned
+ * on failure.
+ *
+ ****************************************************************************/
+
+static int arm64_tick_max_delay(struct oneshot_lowerhalf_s *lower,
+ clock_t *ticks)
+{
+ DEBUGASSERT(ticks != NULL);
- spin_unlock_irqrestore(&g_arch_timer_lock, flags);
+ *ticks = UINT64_MAX;
- nxsched_process_timer();
return OK;
}
-#else
+/****************************************************************************
+ * Name: arm64_tick_cancel
+ *
+ * Description:
+ * Cancel the oneshot timer and return the time remaining on the timer.
+ *
+ * NOTE: This function may execute at a high rate with no timer running (as
+ * when pre-emption is enabled and disabled).
+ *
+ * Input Parameters:
+ * lower Caller allocated instance of the oneshot state structure. This
+ * structure must have been previously initialized via a call to
+ * oneshot_initialize();
+ * ticks The location in which to return the time remaining on the
+ * oneshot timer.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success. A call to up_timer_cancel() when
+ * the timer is not active should also return success; a negated errno
+ * value is returned on any failure.
+ *
+ ****************************************************************************/
-static int arm64_arch_timer_compare_isr(int irq, void *regs, void *arg)
+static int arm64_tick_cancel(struct oneshot_lowerhalf_s *lower,
+ clock_t *ticks)
{
- uint64_t curr_cycle;
- uint64_t delta_ticks;
- uint64_t next_cycle;
+ struct arm64_oneshot_lowerhalf_s *priv =
+ (struct arm64_oneshot_lowerhalf_s *)lower;
- UNUSED(irq);
- UNUSED(regs);
- UNUSED(arg);
+ DEBUGASSERT(priv != NULL && ticks != NULL);
- curr_cycle = arm64_arch_timer_count();
- delta_ticks = (curr_cycle - last_cycle) / cycle_per_tick;
+ /* Disable int */
- last_cycle += delta_ticks * cycle_per_tick;
+ arm64_arch_timer_set_irq_mask(true);
- next_cycle = last_cycle + cycle_per_tick;
+ return OK;
+}
- if ((uint64_t)(next_cycle - curr_cycle) < MIN_DELAY)
- {
- next_cycle += cycle_per_tick;
- }
+/****************************************************************************
+ * Name: arm64_tick_start
+ *
+ * Description:
+ * Start the oneshot timer
+ *
+ * Input Parameters:
+ * lower An instance of the lower-half oneshot state structure. This
+ * structure must have been previously initialized via a call to
+ * oneshot_initialize();
+ * handler The function to call when when the oneshot timer expires.
+ * arg An opaque argument that will accompany the callback.
+ * ticks Provides the duration of the one shot timer.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; a negated errno value is returned
+ * on failure.
+ *
+ ****************************************************************************/
- arm64_arch_timer_set_compare(next_cycle);
+static int arm64_tick_start(struct oneshot_lowerhalf_s *lower,
+ oneshot_callback_t callback, void *arg,
+ clock_t ticks)
+{
+ struct arm64_oneshot_lowerhalf_s *priv =
+ (struct arm64_oneshot_lowerhalf_s *)lower;
+
+ DEBUGASSERT(priv != NULL && callback != NULL);
+
+ /* Save the new handler and its argument */
+
+ priv->callback = callback;
+ priv->arg = arg;
+
+ /* Set the timeout */
+
+ arm64_arch_timer_set_compare(arm64_arch_timer_count() +
+ priv->cycle_per_tick * ticks);
arm64_arch_timer_set_irq_mask(false);
- nxsched_process_timer();
return OK;
}
-#endif
+/****************************************************************************
+ * Name: arm64_tick_current
+ *
+ * Description:
+ * Get the current time.
+ *
+ * Input Parameters:
+ * lower Caller allocated instance of the oneshot state structure. This
+ * structure must have been previously initialized via a call to
+ * oneshot_initialize();
+ * ticks The location in which to return the current time.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success, a negated errno value is returned on
+ * any failure.
+ *
+ ****************************************************************************/
+
+static int arm64_tick_current(struct oneshot_lowerhalf_s *lower,
+ clock_t *ticks)
+{
+ struct arm64_oneshot_lowerhalf_s *priv =
+ (struct arm64_oneshot_lowerhalf_s *)lower;
+
+ DEBUGASSERT(ticks != NULL);
+
+ *ticks = arm64_arch_timer_count() / priv->cycle_per_tick;
+
+ return OK;
+}
/****************************************************************************
- * Public Functions
+ * Private Data
****************************************************************************/
-#ifdef CONFIG_SMP
-/* Notes:
+static const struct oneshot_operations_s g_oneshot_ops =
+{
+ .tick_start = arm64_tick_start,
+ .tick_current = arm64_tick_current,
+ .tick_max_delay = arm64_tick_max_delay,
+ .tick_cancel = arm64_tick_cancel,
+};
+
+/****************************************************************************
+ * Name: oneshot_initialize
*
- * The origin design for ARMv8-A timer is assigned private timer to
- * every PE(CPU core), the ARM_ARCH_TIMER_IRQ is a PPI so it's
- * should be enable at every core.
+ * Description:
+ * Initialize the oneshot timer and return a oneshot lower half driver
+ * instance.
*
- * But for NuttX, it's design only for primary core to handle timer
- * interrupt and call nxsched_process_timer at timer tick mode.
- * So we need only enable timer for primary core
+ * Returned Value:
+ * On success, a non-NULL instance of the oneshot lower-half driver is
+ * returned. NULL is return on any failure.
*
- * IMX6 use GPT which is a SPI rather than generic timer to handle
- * timer interrupt
- */
+ ****************************************************************************/
-void arm64_smp_timer_init(void)
+static struct oneshot_lowerhalf_s *arm64_oneshot_initialize(void)
{
- uint64_t curr_cycle;
+ struct arm64_oneshot_lowerhalf_s *priv;
- /* set the initial status of timer0 of each secondary core */
+ tmrinfo("oneshot_initialize\n");
- curr_cycle = arm64_arch_timer_count();
+ /* Allocate an instance of the lower half driver */
+
+ priv = (struct arm64_oneshot_lowerhalf_s *)
+ kmm_zalloc(sizeof(struct arm64_oneshot_lowerhalf_s));
+
+ if (priv == NULL)
+ {
+ tmrerr("ERROR: Failed to initialized state structure\n");
+
+ return NULL;
+ }
+
+ /* Initialize the lower-half driver structure */
+
+ priv->lh.ops = &g_oneshot_ops;
+ priv->cycle_per_tick = arm64_arch_timer_get_cntfrq() / TICK_PER_SEC;
+ tmrinfo("cycle_per_tick %" PRIu64 "\n", priv->cycle_per_tick);
+
+ /* Attach handler */
+
+ irq_attach(ARM_ARCH_TIMER_IRQ,
+ arm64_arch_timer_compare_isr, priv);
+
+ /* Enable int */
- arm64_arch_timer_set_compare(curr_cycle + cycle_per_tick);
- arm64_arch_timer_enable(true);
up_enable_irq(ARM_ARCH_TIMER_IRQ);
- arm64_arch_timer_set_irq_mask(false);
-}
-#endif
+ /* Start timer */
-uint64_t arm64_counter_read(void)
-{
- return arm64_arch_timer_count();
+ arm64_arch_timer_enable(true);
+
+ tmrinfo("oneshot_initialize ok %p \n", &priv->lh);
+
+ return &priv->lh;
}
/****************************************************************************
- * Name: up_timer_initialize
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: up_timer_initialize
*
* Description:
+ * This function is called during start-up to initialize the system timer
+ * interrupt.
*
****************************************************************************/
void up_timer_initialize(void)
{
- uint64_t curr_cycle;
+ uint64_t freq;
- arch_timer_rate = arm64_arch_timer_get_cntfrq();
- cycle_per_tick = ((uint64_t)arch_timer_rate / (uint64_t)TICK_PER_SEC);
+ freq = arm64_arch_timer_get_cntfrq();
+ tmrinfo("%s: cp15 timer(s) running at %" PRIu64 ".%" PRIu64 "MHz\n",
+ __func__, freq / 1000000, (freq / 10000) % 100);
- sinfo("%s: cp15 timer(s) running at %lu.%02luMHz, cycle %ld\n", __func__,
- (unsigned long)arch_timer_rate / 1000000,
- (unsigned long)(arch_timer_rate / 10000) % 100, cycle_per_tick);
+ up_alarm_set_lowerhalf(arm64_oneshot_initialize());
+}
+
+#ifdef CONFIG_SMP
+/****************************************************************************
+ * Function: arm64_arch_timer_secondary_init
+ *
+ * Description:
+ * This function is called during start-up to initialize the system timer
+ * interrupt for smp.
+ *
+ * Notes:
+ * The origin design for ARMv8-A timer is assigned private timer to
+ * every PE(CPU core), the ARM_ARCH_TIMER_IRQ is a PPI so it's
+ * should be enable at every core.
+ *
+ * But for NuttX, it's design only for primary core to handle timer
+ * interrupt and call nxsched_process_timer at timer tick mode.
+ * So we need only enable timer for primary core
+ *
+ * IMX6 use GPT which is a SPI rather than generic timer to handle
+ * timer interrupt
+ ****************************************************************************/
- irq_attach(ARM_ARCH_TIMER_IRQ, arm64_arch_timer_compare_isr, 0);
+void arm64_arch_timer_secondary_init()
+{
+#ifdef CONFIG_SCHED_TICKLESS
+ tmrinfo("arm64_arch_timer_secondary_init\n");
- curr_cycle = arm64_arch_timer_count();
- arm64_arch_timer_set_compare(curr_cycle + cycle_per_tick);
- arm64_arch_timer_enable(true);
+ /* Enable int */
up_enable_irq(ARM_ARCH_TIMER_IRQ);
- arm64_arch_timer_set_irq_mask(false);
+
+ /* Start timer */
+
+ arm64_arch_timer_enable(true);
+#endif
}
+#endif
\ No newline at end of file
diff --git a/arch/arm64/src/common/arm64_arch_timer.h b/arch/arm64/src/common/arm64_arch_timer.h
index 5fc8a7db99..4cdb2305a3 100644
--- a/arch/arm64/src/common/arm64_arch_timer.h
+++ b/arch/arm64/src/common/arm64_arch_timer.h
@@ -43,12 +43,8 @@
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
-
-extern uint64_t arm64_counter_read(void);
-extern void arm64_start_timer(void);
-
#ifdef CONFIG_SMP
-void arm64_smp_timer_init(void);
+void arm64_arch_timer_secondary_init(void);
#endif
#endif /* __ARCH_ARM64_SRC_COMMON_ARM64_ARCH_TIMER_H */
diff --git a/arch/arm64/src/common/arm64_cpustart.c b/arch/arm64/src/common/arm64_cpustart.c
index b1f023d338..0986f95786 100644
--- a/arch/arm64/src/common/arm64_cpustart.c
+++ b/arch/arm64/src/common/arm64_cpustart.c
@@ -268,6 +268,8 @@ void arm64_boot_secondary_c_routine(void)
arm64_gic_secondary_init();
+ arm64_arch_timer_secondary_init();
+
up_perf_init(NULL);
up_enable_irq(SGI_CPU_PAUSE);