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 2020/07/18 17:23:14 UTC

[incubator-nuttx] 01/02: arch/stm32f7: Fixes bug in tickless driver where the compare register is set to a value less than the current time.

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/incubator-nuttx.git

commit e5be32ac211fd1634681e8ac5a8b438f14ecdd66
Author: Anthony Merlino <an...@vergeaero.com>
AuthorDate: Tue Jul 7 16:02:04 2020 -0400

    arch/stm32f7: Fixes bug in tickless driver where the compare register is set to a value less than the current time.
---
 arch/arm/src/stm32f7/stm32_tickless.c | 45 ++++++++++++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 6 deletions(-)

diff --git a/arch/arm/src/stm32f7/stm32_tickless.c b/arch/arm/src/stm32f7/stm32_tickless.c
index 977410a..02a29c3 100644
--- a/arch/arm/src/stm32f7/stm32_tickless.c
+++ b/arch/arm/src/stm32f7/stm32_tickless.c
@@ -139,7 +139,7 @@ struct stm32_tickless_s
   volatile bool pending;           /* True: pending task */
   uint32_t period;                 /* Interval period */
   uint32_t base;
-#if CONFIG_SCHED_TICKLESS_ALARM
+#ifdef CONFIG_SCHED_TICKLESS_ALARM
   uint64_t last_alrm;
 #endif
 };
@@ -394,6 +394,17 @@ static int stm32_tickless_handler(int irq, void *context, void *arg)
 }
 
 /****************************************************************************
+ * Name: stm32_get_counter
+ *
+ ****************************************************************************/
+
+static uint64_t stm32_get_counter(void)
+{
+  return ((uint64_t)g_tickless.overflow << 32) |
+         STM32_TIM_GETCOUNTER(g_tickless.tch);
+}
+
+/****************************************************************************
  * Public Functions
  ****************************************************************************/
 
@@ -988,22 +999,43 @@ int up_timer_start(FAR const struct timespec *ts)
 }
 #endif
 
+#ifdef CONFIG_SCHED_TICKLESS_ALARM
 int up_alarm_start(FAR const struct timespec *ts)
 {
+  size_t offset = 1;
   uint64_t tm = ((uint64_t)ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec) /
                 NSEC_PER_TICK;
-  uint64_t counter = ((uint64_t)g_tickless.overflow << 32) |
-                     STM32_TIM_GETCOUNTER(g_tickless.tch);
-
-  g_tickless.last_alrm = tm;
+  irqstate_t flags;
 
-  int32_t diff = tm / NSEC_PER_TICK + counter;
+  flags = enter_critical_section();
 
   STM32_TIM_SETCOMPARE(g_tickless.tch, CONFIG_STM32F7_TICKLESS_CHANNEL, tm);
 
   stm32_tickless_ackint(g_tickless.channel);
   stm32_tickless_enableint(CONFIG_STM32F7_TICKLESS_CHANNEL);
 
+  g_tickless.pending = true;
+
+  /* We must protect for a race condition here with very small differences
+   * between the time of the alarm and the time now. We must ensure that the
+   * compare register is set, and interrupts are enabled BEFORE the rising edge
+   * of the clock when COUNT==COMPARE. Otherwise, we cannot be sure we are 
+   * going to get the interrupt. If we didn't catch this case, we wouldn't
+   * interrupt until a full loop of the clock.
+   * 
+   * Since we can't make assumptions about the clock speed and tick rate,
+   * we simply keep adding an offset to the current time, until we can leave
+   * certain that the interrupt is going to fire as soon as we leave the
+   * critical section.
+   */
+
+  while (tm <= stm32_get_counter())
+    {
+      tm = stm32_get_counter() + offset++;
+      STM32_TIM_SETCOMPARE(g_tickless.tch, CONFIG_STM32F7_TICKLESS_CHANNEL, tm);
+    }
+
+  leave_critical_section(flags);
   return OK;
 }
 
@@ -1019,5 +1051,6 @@ int up_alarm_cancel(FAR struct timespec *ts)
 
   return 0;
 }
+#endif
 
 #endif /* CONFIG_SCHED_TICKLESS */