Browse Source

drivers/nrf5: add watchdog timer support

Signed-off-by: Joshua Wise <joshua@accelerated.tech>
Joshua Wise 4 months ago
parent
commit
caa768ac76

BIN
bin/boot/boot_asterix_evt1@1743463209.bin → bin/boot/boot_asterix_evt1@1743464459.bin


BIN
bin/boot/boot_asterix_evt1@1743463209.elf → bin/boot/boot_asterix_evt1@1743464459.elf


+ 1 - 1
bin/boot/boot_asterix_evt1@1743463209.hex → bin/boot/boot_asterix_evt1@1743464459.hex

@@ -391,7 +391,7 @@
 :101850000023C8F804350120EFE7D8F80034DA0790
 :10186000FBD559F80430A3512FB1630603D1089A70
 :101870005146E009B8470434E8E700BF030300001D
-:1018800000E00140044B0122C3F808254FF4003268
+:1018800000E00140044B0122C3F808254FF48022F8
 :10189000C3F80425704700BF00000140014B01223E
 :1018A0001A607047000001404FF080434FF0FF3254
 :1018B000D3F80004C3F80024C0F340007047490087

BIN
bin/boot/nowatchdog_boot_asterix_evt1@1743463209.bin → bin/boot/nowatchdog_boot_asterix_evt1@1743464459.bin


BIN
bin/boot/nowatchdog_boot_asterix_evt1@1743463209.elf → bin/boot/nowatchdog_boot_asterix_evt1@1743464459.elf


+ 1 - 1
bin/boot/nowatchdog_boot_asterix_evt1@1743463209.hex → bin/boot/nowatchdog_boot_asterix_evt1@1743464459.hex

@@ -390,7 +390,7 @@
 :101840000023C8F804350120EFE7D8F80034DA07A0
 :10185000FBD559F80430A3512FB1630603D1089A80
 :101860005146E009B8470434E8E700BF030300002D
-:1018700000E00140044B0122C3F808254FF4003278
+:1018700000E00140044B0122C3F808254FF4802208
 :10188000C3F80425704700BF000001404FF08043BB
 :101890004FF0FF32D3F80004C3F80024C0F3400037
 :1018A00070474900FFF79EBD2DE9F047012186B042

+ 2 - 2
platform/asterix/boot/src/drivers/watchdog.c

@@ -5,8 +5,8 @@
 
 void watchdog_init(void) {
 	NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos;
-	/* WDT expiration: 4s */
-	NRF_WDT->CRV = 32768 * 4;
+	/* WDT expiration: 8s */
+	NRF_WDT->CRV = 32768 * 8;
 }
 
 void watchdog_start(void) {

+ 0 - 36
src/fw/drivers/nrf5/stubs/watchdog.c

@@ -1,36 +0,0 @@
-#include "drivers/watchdog.h"
-
-#include "util/bitset.h"
-#include "system/logging.h"
-
-#define NRF5_COMPATIBLE
-#include <mcu.h>
-
-#include <inttypes.h>
-
-void watchdog_init(void) {
-}
-
-void watchdog_start(void) {
-}
-
-void watchdog_feed(void) {
-}
-
-bool watchdog_check_reset_flag(void) {
-  return 0;
-}
-
-McuRebootReason watchdog_clear_reset_flag(void) {
-  McuRebootReason mcu_reboot_reason = {
-    .brown_out_reset = 0,
-    .pin_reset = 0,
-    .power_on_reset = 0,
-    .software_reset = 0,
-    .independent_watchdog_reset = 0,
-    .window_watchdog_reset = 0,
-    .low_power_manager_reset = 0,
-  };
-
-  return mcu_reboot_reason;
-}

+ 48 - 0
src/fw/drivers/nrf5/watchdog.c

@@ -0,0 +1,48 @@
+#include "drivers/watchdog.h"
+
+#include "util/bitset.h"
+#include "system/logging.h"
+
+#define NRF5_COMPATIBLE
+#include <mcu.h>
+
+#include <nrfx.h>
+#include <helpers/nrfx_reset_reason.h>
+#include <hal/nrf_wdt.h>
+
+#include <inttypes.h>
+
+void watchdog_init(void) {
+  nrf_wdt_reload_request_enable(NRF_WDT, NRF_WDT_RR0);
+  /* WDT expiration: 8s */
+  nrf_wdt_reload_value_set(NRF_WDT, 32768 * 8);
+}
+
+void watchdog_start(void) {
+  nrf_wdt_task_trigger(NRF_WDT, NRF_WDT_TASK_START);
+}
+
+void watchdog_feed(void) {
+  nrf_wdt_reload_request_set(NRF_WDT, NRF_WDT_RR0);
+}
+
+bool watchdog_check_reset_flag(void) {
+  return (nrfx_reset_reason_get() & NRFX_RESET_REASON_DOG_MASK) != 0;
+}
+
+McuRebootReason watchdog_clear_reset_flag(void) {
+  uint32_t reason = nrfx_reset_reason_get();
+  nrfx_reset_reason_clear(0xFFFFFFFF);
+
+  McuRebootReason mcu_reboot_reason = {
+    .brown_out_reset = 0,
+    .pin_reset = (reason & NRFX_RESET_REASON_RESETPIN_MASK) != 0,
+    .power_on_reset = (reason & NRFX_RESET_REASON_VBUS_MASK) != 0,
+    .software_reset = (reason & NRFX_RESET_REASON_SREQ_MASK) != 0,
+    .independent_watchdog_reset = (reason & NRFX_RESET_REASON_DOG_MASK) != 0,
+    .window_watchdog_reset = 0,
+    .low_power_manager_reset = 0,
+  };
+
+  return mcu_reboot_reason;
+}

+ 47 - 15
src/fw/drivers/task_watchdog.c

@@ -49,6 +49,10 @@
 #include "debug/setup.h"
 #endif
 
+#if MICRO_FAMILY_NRF5
+#include <hal/nrf_rtc.h>
+#endif
+
 #define APP_THROTTLE_TIME_MS 300
 
 // These bits get set by calls to task_watchdog_bit_set and checked and cleared periodically by our watchdog feed
@@ -75,8 +79,13 @@ static TimerID s_throttle_timer_id = TIMER_INVALID_ID;
 static uint8_t s_ticks_since_successful_feed = 0;
 
 // We use this interrupt vector for our lower priority interrupts
+#if MICRO_FAMILY_NRF5
+#define WATCHDOG_FREERTOS_IRQn        EGU5_SWI5_IRQn
+#define WATCHDOG_FREERTOS_IRQHandler  EGU5_SWI5_IRQHandler
+#else
 #define WATCHDOG_FREERTOS_IRQn        CAN2_SCE_IRQn
 #define WATCHDOG_FREERTOS_IRQHandler  CAN2_SCE_IRQHandler
+#endif
 
 static void prv_task_watchdog_feed(void);
 
@@ -150,7 +159,17 @@ static void prv_log_failed_message(RebootReason *reboot_reason) {
 // -------------------------------------------------------------------------------------------------
 // The Timer ISR. This runs at super high priority (higher than configMAX_SYSCALL_INTERRUPT_PRIORITY), so
 // it is not safe to call ANY FreeRTOS functions from here.
-#if !MICRO_FAMILY_NRF5
+#if MICRO_FAMILY_NRF5
+void RTC2_IRQHandler(void) {
+  nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0);
+  nrf_rtc_task_trigger(NRF_RTC2, NRF_RTC_TASK_CLEAR);
+  nrf_rtc_int_enable(NRF_RTC2, NRF_RTC_INT_COMPARE0_MASK);
+  nrf_rtc_event_enable(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0);
+
+  s_ticks_since_successful_feed++;
+  prv_task_watchdog_feed();
+}
+#else
 void TIM2_IRQHandler(void) {
   // Workaround M3 bug that causes interrupt to fire twice:
   // https://my.st.com/public/Faq/Lists/faqlst/DispForm.aspx?ID=143
@@ -253,6 +272,20 @@ void WATCHDOG_FREERTOS_IRQHandler(void) {
 // which resets the watchdog timer if it detects that none of our watchable tasks are stuck.
 void task_watchdog_init(void) {
 #if MICRO_FAMILY_NRF5
+  // We use RTC2 as the WDT kicker; RTC1 is used by the OS RTC
+  nrf_rtc_prescaler_set(NRF_RTC2, NRF_RTC_FREQ_TO_PRESCALER(TIMER_CLOCK_HZ));
+
+  // trigger compare interrupt at appropriate time
+  nrf_rtc_cc_set(NRF_RTC2, 0, TIME_PERIOD);
+  nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0);
+  nrf_rtc_int_enable(NRF_RTC2, NRF_RTC_INT_COMPARE0_MASK);
+  nrf_rtc_event_enable(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0);
+
+  NVIC_SetPriority(RTC2_IRQn, TASK_WATCHDOG_PRIORITY << 4);
+  NVIC_ClearPendingIRQ(RTC2_IRQn);
+  NVIC_EnableIRQ(RTC2_IRQn);
+
+  nrf_rtc_task_trigger(NRF_RTC2, NRF_RTC_TASK_START);
 #else
   // The timer is on ABP1 which is clocked by PCLK1
   RCC_ClocksTypeDef clocks;
@@ -295,25 +328,31 @@ void task_watchdog_init(void) {
 
   TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
   TIM_Cmd(TIM2, ENABLE);
+#endif
 
   // Setup another unused interrupt vector to handle our low priority interrupts. When we need to do higher
   // level functions (like PBL_LOG), we trigger this lower-priority interrupt to fire. Since it runs at
   // configMAX_SYSCALL_INTERRUPT_PRIORITY or lower, it can at least call FreeRTOS ISR functions.
+#if MICRO_FAMILY_NRF5
+  NVIC_SetPriority(WATCHDOG_FREERTOS_IRQn, configMAX_SYSCALL_INTERRUPT_PRIORITY);
+#else
   NVIC_InitStructure.NVIC_IRQChannel = WATCHDOG_FREERTOS_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
+#endif
 
   NVIC_EnableIRQ(WATCHDOG_FREERTOS_IRQn);
-#endif
 
   // create the app throttling timer
   s_throttle_timer_id = new_timer_create();
 }
 
 static void task_watchdog_disable_interrupt() {
-#if !MICRO_FAMILY_NRF5
+#if MICRO_FAMILY_NRF5
+  NVIC_DisableIRQ(RTC2_IRQn);
+#else
   NVIC_DisableIRQ(TIM2_IRQn);
 #endif
   taskENTER_CRITICAL();
@@ -321,7 +360,9 @@ static void task_watchdog_disable_interrupt() {
 
 static void task_watchdog_enable_interrupt() {
   taskEXIT_CRITICAL();
-#if !MICRO_FAMILY_NRF5
+#if MICRO_FAMILY_NRF5
+  NVIC_EnableIRQ(RTC2_IRQn);
+#else
   NVIC_EnableIRQ(TIM2_IRQn);
 #endif
 }
@@ -358,24 +399,19 @@ void task_watchdog_mask_clear(PebbleTask task) {
 }
 
 void task_watchdog_step_elapsed_time_ms(uint32_t elapsed_ms) {
-  uint32_t timer_ticks = (elapsed_ms * TIMER_CLOCK_HZ) / 1000;
+  // nRF5 has the RTC running during sleep, and needs no help here
 #if !MICRO_FAMILY_NRF5
+  uint32_t timer_ticks = (elapsed_ms * TIMER_CLOCK_HZ) / 1000;
   timer_ticks += TIM2->CNT;
-#endif
 
   uint8_t timer_ticks_elapsed = timer_ticks / TIME_PERIOD;
   if (timer_ticks_elapsed > 0) {
     // we don't want the interrupt to fire while we are editing the feed count
-#if !MICRO_FAMILY_NRF5
     TIM_Cmd(TIM2, DISABLE);
-#endif
     s_ticks_since_successful_feed += timer_ticks_elapsed;
-#if !MICRO_FAMILY_NRF5
     TIM_Cmd(TIM2, ENABLE);
-#endif
   }
 
-#if !MICRO_FAMILY_NRF5
   TIM2->CNT = timer_ticks % TIME_PERIOD;
 #endif
   prv_task_watchdog_feed();
@@ -411,9 +447,7 @@ static void prv_task_watchdog_feed(void) {
       reboot_reason_clear();
       // Trigger our lower priority interrupt to fire. If it fires when reboot reason is not RebootReasonCode_Watchdog,
       //  it simply logs a message that the we recovered from a watchdog stall
-#if !MICRO_FAMILY_NRF5
       NVIC_SetPendingIRQ(WATCHDOG_FREERTOS_IRQn);
-#endif
       s_last_warning_message_tick_time = 0;
     }
 
@@ -442,9 +476,7 @@ static void prv_task_watchdog_feed(void) {
     // Trigger our lower priority interrupt to fire. When it sees
     //  RebootReasonCode_Watchdog in the reboot reason, it logs information
     //  about the stuck task
-#if !MICRO_FAMILY_NRF5
     NVIC_SetPendingIRQ(WATCHDOG_FREERTOS_IRQn);
-#endif
 
     // If the low priority interrupt hasn't reset us by the time 6.5 seconds
     // rolls around (it will issue the reset if executed at least 6 seconds

+ 1 - 1
src/fw/drivers/wscript_build

@@ -1178,7 +1178,7 @@ if mcu_family == 'NRF52840':
             'flash/xt25f64b.c',
             'nrf5/qspi.c',
             'task_watchdog.c',
-            'nrf5/stubs/watchdog.c',
+            'nrf5/watchdog.c',
             'display/sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c',
             'nrf5/stubs/accel.c',
             'nrf5/stubs/ambient_light.c',