summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Shawcroft <scott@adafruit.com>2020-06-02 10:33:24 -0700
committerGitHub <noreply@github.com>2020-06-02 10:33:24 -0700
commitdb04fd1725d375d7f9110c35f9346b32cb99b19e (patch)
tree652ffb11481a29bd424de8de05a8ab42ab042b99
parentaae7a459f37c284445eb68b5d691238f212a12a4 (diff)
parent7e632c9414073a36db0b74d891ee0408a2fdb565 (diff)
Merge pull request #2933 from simmel-project/wdt-nrf
Add Watchdog timer and add support for NRF watchdog
-rw-r--r--locale/circuitpython.pot30
-rwxr-xr-xports/nrf/Makefile1
-rw-r--r--ports/nrf/boards/common.template.ld22
-rw-r--r--ports/nrf/boards/pca10100/mpconfigboard.mk6
-rw-r--r--ports/nrf/boards/simmel/mpconfigboard.h2
-rw-r--r--ports/nrf/boards/simmel/mpconfigboard.mk1
-rw-r--r--ports/nrf/common-hal/microcontroller/__init__.c14
-rw-r--r--ports/nrf/common-hal/watchdog/WatchDogMode.c0
-rw-r--r--ports/nrf/common-hal/watchdog/WatchDogTimer.c189
-rw-r--r--ports/nrf/common-hal/watchdog/WatchDogTimer.h44
-rw-r--r--ports/nrf/common-hal/watchdog/__init__.c25
-rw-r--r--ports/nrf/common-hal/watchdog/__init__.h30
-rw-r--r--ports/nrf/mpconfigport.mk1
-rw-r--r--ports/nrf/nrfx_config.h7
-rw-r--r--ports/nrf/nrfx_glue.h8
-rw-r--r--ports/nrf/supervisor/port.c47
-rw-r--r--py/circuitpy_defns.mk8
-rw-r--r--py/circuitpy_mpconfig.h9
-rw-r--r--py/circuitpy_mpconfig.mk4
-rw-r--r--shared-bindings/microcontroller/__init__.c5
-rw-r--r--shared-bindings/microcontroller/__init__.h5
-rw-r--r--shared-bindings/watchdog/WatchDogMode.c99
-rw-r--r--shared-bindings/watchdog/WatchDogMode.h49
-rw-r--r--shared-bindings/watchdog/WatchDogTimer.c186
-rw-r--r--shared-bindings/watchdog/WatchDogTimer.h48
-rw-r--r--shared-bindings/watchdog/__init__.c81
-rw-r--r--shared-bindings/watchdog/__init__.h34
-rw-r--r--supervisor/shared/safe_mode.c3
-rw-r--r--supervisor/shared/safe_mode.h1
-rw-r--r--supervisor/shared/tick.c10
30 files changed, 941 insertions, 28 deletions
diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot
index 8ccfe64bd..11ecb99e8 100644
--- a/locale/circuitpython.pot
+++ b/locale/circuitpython.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-06-01 08:56-0500\n"
+"POT-Creation-Date: 2020-06-01 17:10-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1652,6 +1652,26 @@ msgstr ""
msgid "WARNING: Your code filename has two extensions\n"
msgstr ""
+#: shared-bindings/watchdog/WatchDogTimer.c
+msgid "WatchDogTimer cannot be deinitialized once mode is set to RESET"
+msgstr ""
+
+#: shared-bindings/watchdog/WatchDogTimer.c
+msgid "WatchDogTimer is not currently running"
+msgstr ""
+
+#: shared-bindings/watchdog/WatchDogTimer.c
+msgid "WatchDogTimer.mode cannot be changed once set to WatchDogMode.RESET"
+msgstr ""
+
+#: shared-bindings/watchdog/WatchDogTimer.c
+msgid "WatchDogTimer.timeout must be greater than 0"
+msgstr ""
+
+#: supervisor/shared/safe_mode.c
+msgid "Watchdog timer expired."
+msgstr ""
+
#: py/builtinhelp.c
#, c-format
msgid ""
@@ -3059,6 +3079,10 @@ msgstr ""
msgid "time.struct_time() takes a 9-sequence"
msgstr ""
+#: ports/nrf/common-hal/watchdog/WatchDogTimer.c
+msgid "timeout duration exceeded the maximum supported value"
+msgstr ""
+
#: shared-bindings/busio/UART.c
msgid "timeout must be 0.0-100.0 seconds"
msgstr ""
@@ -3212,6 +3236,10 @@ msgstr ""
msgid "value_count must be > 0"
msgstr ""
+#: shared-bindings/watchdog/WatchDogTimer.c
+msgid "watchdog timeout must be greater than 0"
+msgstr ""
+
#: shared-bindings/_bleio/Adapter.c
msgid "window must be <= interval"
msgstr ""
diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile
index 44d1f3de2..5b81fa9a7 100755
--- a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -136,6 +136,7 @@ SRC_NRFX = $(addprefix nrfx/,\
drivers/src/nrfx_gpiote.c \
drivers/src/nrfx_rtc.c \
drivers/src/nrfx_nvmc.c \
+ drivers/src/nrfx_wdt.c \
)
ifdef EXTERNAL_FLASH_DEVICES
diff --git a/ports/nrf/boards/common.template.ld b/ports/nrf/boards/common.template.ld
index 9a7df69a4..5982b8ba0 100644
--- a/ports/nrf/boards/common.template.ld
+++ b/ports/nrf/boards/common.template.ld
@@ -70,20 +70,6 @@ SECTIONS
_etext = .; /* define a global symbol at end of code */
} >FLASH_FIRMWARE
- /*
- .ARM.extab :
- {
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- } >FLASH_FIRMWARE
-
- .ARM :
- {
- __exidx_start = .;
- *(.ARM.exidx*)
- __exidx_end = .;
- } >FLASH_FIRMWARE
- */
-
/* used by the startup to initialize data */
_sidata = .;
@@ -147,6 +133,14 @@ SECTIONS
. = ALIGN(4);
} >RAM
+ /* Remove exception unwinding information, since Circuit Python
+ does not support this GCC feature. */
+ /DISCARD/ :
+ {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ *(.ARM.exidx*)
+ }
+
/* Remove information from the standard libraries */
/*
/DISCARD/ :
diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk
index 1206c8fe5..715b65e63 100644
--- a/ports/nrf/boards/pca10100/mpconfigboard.mk
+++ b/ports/nrf/boards/pca10100/mpconfigboard.mk
@@ -8,9 +8,13 @@ MCU_CHIP = nrf52833
INTERNAL_FLASH_FILESYSTEM = 1
CIRCUITPY_AUDIOMP3 = 0
+CIRCUITPY_BITBANGIO = 0
CIRCUITPY_BUSIO = 1
+CIRCUITPY_COUNTIO = 0
CIRCUITPY_DISPLAYIO = 0
CIRCUITPY_FRAMEBUFFERIO = 0
+CIRCUITPY_FREQUENCYIO = 0
+CIRCUITPY_I2CSLAVE = 0
CIRCUITPY_NEOPIXEL_WRITE = 0
CIRCUITPY_NVM = 0
CIRCUITPY_PIXELBUF = 0
@@ -20,6 +24,8 @@ CIRCUITPY_RTC = 1
CIRCUITPY_TOUCHIO = 0
CIRCUITPY_ULAB = 0
+SUPEROPT_GC = 0
+
# These defines must be overridden before mpconfigboard.h is included, which is
# why they are passed on the command line.
CFLAGS += -DSPIM3_BUFFER_SIZE=0 -DSOFTDEVICE_RAM_SIZE='(32*1024)'
diff --git a/ports/nrf/boards/simmel/mpconfigboard.h b/ports/nrf/boards/simmel/mpconfigboard.h
index bcffb40ea..28c4ee7d9 100644
--- a/ports/nrf/boards/simmel/mpconfigboard.h
+++ b/ports/nrf/boards/simmel/mpconfigboard.h
@@ -40,7 +40,7 @@
#endif
#define CIRCUITPY_INTERNAL_NVM_SIZE 0
-#define CIRCUITPY_INTERNAL_FLASH_FILESYSTEM_SIZE (80*1024)
+#define CIRCUITPY_INTERNAL_FLASH_FILESYSTEM_SIZE (76*1024)
#define BOOTLOADER_SIZE (0x4000) // 12 kiB
#define CIRCUITPY_BLE_CONFIG_SIZE (12*1024)
diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk
index 36a6fadda..2bca2492f 100644
--- a/ports/nrf/boards/simmel/mpconfigboard.mk
+++ b/ports/nrf/boards/simmel/mpconfigboard.mk
@@ -24,6 +24,7 @@ CIRCUITPY_ROTARYIO = 0
CIRCUITPY_RTC = 1
CIRCUITPY_TOUCHIO = 0
CIRCUITPY_ULAB = 0
+CIRCUITPY_WATCHDOG = 1
# Enable micropython.native
#CIRCUITPY_ENABLE_MPY_NATIVE = 1
diff --git a/ports/nrf/common-hal/microcontroller/__init__.c b/ports/nrf/common-hal/microcontroller/__init__.c
index 90d97f868..f5caf68ef 100644
--- a/ports/nrf/common-hal/microcontroller/__init__.c
+++ b/ports/nrf/common-hal/microcontroller/__init__.c
@@ -94,7 +94,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
void common_hal_mcu_reset(void) {
filesystem_flush();
- NVIC_SystemReset();
+ reset_cpu();
}
// The singleton microcontroller.Processor object, bound to microcontroller.cpu
@@ -106,7 +106,6 @@ const mcu_processor_obj_t common_hal_mcu_processor_obj = {
};
#if CIRCUITPY_INTERNAL_NVM_SIZE > 0
-
// The singleton nvm.ByteArray object.
const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = {
.base = {
@@ -117,6 +116,17 @@ const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = {
};
#endif
+#if CIRCUITPY_WATCHDOG
+// The singleton watchdog.WatchDogTimer object.
+watchdog_watchdogtimer_obj_t common_hal_mcu_watchdogtimer_obj = {
+ .base = {
+ .type = &watchdog_watchdogtimer_type,
+ },
+ .timeout = 0.0f,
+ .mode = WATCHDOGMODE_NONE,
+};
+#endif
+
STATIC const mp_rom_map_elem_t mcu_pin_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_P0_00), MP_ROM_PTR(&pin_P0_00) },
{ MP_ROM_QSTR(MP_QSTR_P0_01), MP_ROM_PTR(&pin_P0_01) },
diff --git a/ports/nrf/common-hal/watchdog/WatchDogMode.c b/ports/nrf/common-hal/watchdog/WatchDogMode.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ports/nrf/common-hal/watchdog/WatchDogMode.c
diff --git a/ports/nrf/common-hal/watchdog/WatchDogTimer.c b/ports/nrf/common-hal/watchdog/WatchDogTimer.c
new file mode 100644
index 000000000..bec0ac473
--- /dev/null
+++ b/ports/nrf/common-hal/watchdog/WatchDogTimer.c
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Nick Moore for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include <math.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#include "common-hal/watchdog/WatchDogTimer.h"
+
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/watchdog/__init__.h"
+#include "shared-bindings/watchdog/WatchDogTimer.h"
+
+#include "supervisor/port.h"
+
+#include "nrf/timers.h"
+#include "nrf_wdt.h"
+#include "nrfx_wdt.h"
+#include "nrfx_timer.h"
+
+STATIC uint8_t timer_refcount = 0;
+STATIC nrfx_timer_t *timer = NULL;
+STATIC nrfx_wdt_t wdt = NRFX_WDT_INSTANCE(0);
+STATIC nrfx_wdt_channel_id wdt_channel_id;
+
+STATIC void watchdogtimer_timer_event_handler(nrf_timer_event_t event_type, void *p_context) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(p_context);
+ if (event_type != NRF_TIMER_EVENT_COMPARE0) {
+ // Spurious event.
+ return;
+ }
+
+ // If the timer hits without being cleared, pause the timer and raise an exception.
+ nrfx_timer_pause(timer);
+ self->mode = WATCHDOGMODE_NONE;
+ mp_obj_exception_clear_traceback(MP_OBJ_FROM_PTR(&mp_watchdog_timeout_exception));
+ MP_STATE_VM(mp_pending_exception) = &mp_watchdog_timeout_exception;
+#if MICROPY_ENABLE_SCHEDULER
+ if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
+ MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
+ }
+#endif
+}
+
+static void timer_free(void) {
+ timer_refcount--;
+ if (timer_refcount == 0) {
+ nrf_peripherals_free_timer(timer);
+ timer = NULL;
+ }
+}
+
+// This function is called if the timer expires. The system will reboot
+// in 1/16384 of a second. Issue a reboot ourselves so we can do any
+// cleanup necessary.
+STATIC void watchdogtimer_watchdog_event_handler(void) {
+ reset_cpu();
+}
+
+void common_hal_watchdog_feed(watchdog_watchdogtimer_obj_t *self) {
+ if (self->mode == WATCHDOGMODE_RESET) {
+ nrfx_wdt_feed(&wdt);
+ } else if (self->mode == WATCHDOGMODE_RAISE) {
+ nrfx_timer_clear(timer);
+ }
+}
+
+void common_hal_watchdog_deinit(watchdog_watchdogtimer_obj_t *self) {
+ if (timer) {
+ timer_free();
+ }
+ self->mode = WATCHDOGMODE_NONE;
+}
+
+void watchdog_reset(void) {
+ common_hal_watchdog_deinit(&common_hal_mcu_watchdogtimer_obj);
+}
+
+mp_float_t common_hal_watchdog_get_timeout(watchdog_watchdogtimer_obj_t *self) {
+ return self->timeout;
+}
+
+void common_hal_watchdog_set_timeout(watchdog_watchdogtimer_obj_t *self, mp_float_t timeout) {
+ if (self->mode == WATCHDOGMODE_RAISE) {
+ // If the WatchDogTimer is already running in "RAISE" mode, reset the timer
+ // with the new value.
+ uint64_t ticks = timeout * 31250ULL;
+ if (ticks > UINT32_MAX) {
+ mp_raise_ValueError(translate("timeout duration exceeded the maximum supported value"));
+ }
+ nrfx_timer_clear(timer);
+ nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL0, ticks, true);
+ }
+
+ self->timeout = timeout;
+}
+
+watchdog_watchdogmode_t common_hal_watchdog_get_mode(watchdog_watchdogtimer_obj_t *self) {
+ return self->mode;
+}
+
+void common_hal_watchdog_set_mode(watchdog_watchdogtimer_obj_t *self, watchdog_watchdogmode_t new_mode) {
+ watchdog_watchdogmode_t current_mode = self->mode;
+
+ if (new_mode == WATCHDOGMODE_RAISE) {
+ if (timer_refcount == 0) {
+ timer = nrf_peripherals_allocate_timer_or_throw();
+ }
+ timer_refcount++;
+
+ nrfx_timer_config_t timer_config = {
+ .frequency = NRF_TIMER_FREQ_31250Hz,
+ .mode = NRF_TIMER_MODE_TIMER,
+ .bit_width = NRF_TIMER_BIT_WIDTH_32,
+ .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
+ .p_context = self,
+ };
+
+ nrfx_timer_init(timer, &timer_config, &watchdogtimer_timer_event_handler);
+
+ uint64_t ticks = nrfx_timer_ms_to_ticks(timer, self->timeout * 1000);
+ if (ticks > UINT32_MAX) {
+ mp_raise_ValueError(translate("timeout duration exceeded the maximum supported value"));
+ }
+
+ // true enables interrupt.
+ nrfx_timer_clear(timer);
+ nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL0, ticks, true);
+ nrfx_timer_resume(timer);
+
+ } else if (new_mode == WATCHDOGMODE_RESET) {
+ uint64_t ticks = self->timeout * 1000.0f;
+ if (ticks > UINT32_MAX) {
+ mp_raise_ValueError(translate("timeout duration exceeded the maximum supported value"));
+ }
+
+ nrfx_wdt_config_t config = {
+ .reload_value = ticks, // in units of ms
+ .behaviour = NRF_WDT_BEHAVIOUR_RUN_SLEEP,
+ NRFX_WDT_IRQ_CONFIG
+ };
+
+ nrfx_err_t err_code;
+ err_code = nrfx_wdt_init(&wdt, &config, watchdogtimer_watchdog_event_handler);
+ if (err_code != NRFX_SUCCESS) {
+ mp_raise_OSError(1);
+ }
+ err_code = nrfx_wdt_channel_alloc(&wdt, &wdt_channel_id);
+ if (err_code != NRFX_SUCCESS) {
+ mp_raise_OSError(1);
+ }
+ nrfx_wdt_enable(&wdt);
+ nrfx_wdt_feed(&wdt);
+ }
+
+ // If we just switched away from RAISE, disable the timmer.
+ if (current_mode == WATCHDOGMODE_RAISE && new_mode != WATCHDOGMODE_RAISE) {
+ timer_free();
+ }
+
+ self->mode = new_mode;
+}
diff --git a/ports/nrf/common-hal/watchdog/WatchDogTimer.h b/ports/nrf/common-hal/watchdog/WatchDogTimer.h
new file mode 100644
index 000000000..8d6df934e
--- /dev/null
+++ b/ports/nrf/common-hal/watchdog/WatchDogTimer.h
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Noralf Trønnes
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG_WATCHDOGTIMER_H
+#define MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG_WATCHDOGTIMER_H
+
+#include "py/obj.h"
+#include "shared-bindings/watchdog/WatchDogMode.h"
+#include "shared-bindings/watchdog/WatchDogTimer.h"
+
+struct _watchdog_watchdogtimer_obj_t {
+ mp_obj_base_t base;
+ mp_float_t timeout;
+ watchdog_watchdogmode_t mode;
+};
+
+// This needs to be called in order to disable the watchdog if it's set to
+// "RAISE". If set to "RESET", then the watchdog cannot be reset.
+void watchdog_reset(void);
+
+#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG_WATCHDOGTIMER_H
diff --git a/ports/nrf/common-hal/watchdog/__init__.c b/ports/nrf/common-hal/watchdog/__init__.c
new file mode 100644
index 000000000..79875d127
--- /dev/null
+++ b/ports/nrf/common-hal/watchdog/__init__.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Nick Moore for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
diff --git a/ports/nrf/common-hal/watchdog/__init__.h b/ports/nrf/common-hal/watchdog/__init__.h
new file mode 100644
index 000000000..de19bdae4
--- /dev/null
+++ b/ports/nrf/common-hal/watchdog/__init__.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Noralf Trønnes
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG___INIT___H
+#define MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG___INIT___H
+
+#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG___INIT___H
diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk
index 474446f5c..dda9b2436 100644
--- a/ports/nrf/mpconfigport.mk
+++ b/ports/nrf/mpconfigport.mk
@@ -37,6 +37,7 @@ CIRCUITPY_RGBMATRIX ?= 1
CIRCUITPY_FRAMEBUFFERIO ?= 1
CIRCUITPY_COUNTIO = 0
+CIRCUITPY_WATCHDOG ?= 1
# nRF52840-specific
diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h
index 7a7f2e6d6..b528a6032 100644
--- a/ports/nrf/nrfx_config.h
+++ b/ports/nrf/nrfx_config.h
@@ -122,4 +122,11 @@
// NVM controller
#define NRFX_NVMC_ENABLED 1
+// Watchdog timer
+#define NRFX_WDT_ENABLED 1
+#define NRFX_WDT0_ENABLED 1
+// This IRQ indicates the system will reboot shortly, so give
+// it a high priority.
+#define NRFX_WDT_DEFAULT_CONFIG_IRQ_PRIORITY 2
+
#endif // NRFX_CONFIG_H__
diff --git a/ports/nrf/nrfx_glue.h b/ports/nrf/nrfx_glue.h
index 345de3870..9f91b72a1 100644
--- a/ports/nrf/nrfx_glue.h
+++ b/ports/nrf/nrfx_glue.h
@@ -180,17 +180,17 @@ static inline bool _NRFX_IRQ_IS_PENDING(IRQn_Type irq_number)
return (NVIC_GetPendingIRQ(irq_number) == 1);
}
-//#include <nordic_common.h>
-//#include <app_util_platform.h>
+void common_hal_mcu_disable_interrupts(void);
+void common_hal_mcu_enable_interrupts(void);
/**
* @brief Macro for entering into a critical section.
*/
-#define NRFX_CRITICAL_SECTION_ENTER() CRITICAL_REGION_ENTER()
+#define NRFX_CRITICAL_SECTION_ENTER() common_hal_mcu_disable_interrupts()
/**
* @brief Macro for exiting from a critical section.
*/
-#define NRFX_CRITICAL_SECTION_EXIT() CRITICAL_REGION_EXIT()
+#define NRFX_CRITICAL_SECTION_EXIT() common_hal_mcu_enable_interrupts()
//------------------------------------------------------------------------------
diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c
index 36725293c..aee2d63e1 100644
--- a/ports/nrf/supervisor/port.c
+++ b/ports/nrf/supervisor/port.c
@@ -50,6 +50,7 @@
#include "common-hal/pulseio/PulseIn.h"
#include "common-hal/rtc/RTC.h"
#include "common-hal/neopixel_write/__init__.h"
+#include "common-hal/watchdog/WatchDogTimer.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/rtc/__init__.h"
@@ -77,13 +78,19 @@ const nrfx_rtc_config_t rtc_config = {
.interrupt_priority = 6
};
-static volatile uint64_t overflowed_ticks = 0;
+#define OVERFLOW_CHECK_PREFIX 0x2cad564f
+#define OVERFLOW_CHECK_SUFFIX 0x11343ef7
+static volatile struct {
+ uint32_t prefix;
+ uint64_t overflowed_ticks;
+ uint32_t suffix;
+} overflow_tracker __attribute__((section(".uninitialized")));
void rtc_handler(nrfx_rtc_int_type_t int_type) {
if (int_type == NRFX_RTC_INT_OVERFLOW) {
// Our RTC is 24 bits and we're clocking it at 32.768khz which is 32 (2 ** 5) subticks per
// tick.
- overflowed_ticks += (1L<< (24 - 5));
+ overflow_tracker.overflowed_ticks += (1L<< (24 - 5));
} else if (int_type == NRFX_RTC_INT_TICK && nrfx_rtc_counter_get(&rtc_instance) % 32 == 0) {
// Do things common to all ports when the tick occurs
supervisor_tick();
@@ -100,6 +107,17 @@ void tick_init(void) {
nrfx_rtc_init(&rtc_instance, &rtc_config, rtc_handler);
nrfx_rtc_enable(&rtc_instance);
nrfx_rtc_overflow_enable(&rtc_instance, true);
+
+ // If the check prefix and suffix aren't correct, then the structure
+ // in memory isn't correct and the clock will be wildly wrong. Initialize
+ // the prefix and suffix so that we know the value is correct, and reset
+ // the time to 0.
+ if (overflow_tracker.prefix != OVERFLOW_CHECK_PREFIX ||
+ overflow_tracker.suffix != OVERFLOW_CHECK_SUFFIX) {
+ overflow_tracker.prefix = OVERFLOW_CHECK_PREFIX;
+ overflow_tracker.suffix = OVERFLOW_CHECK_SUFFIX;
+ overflow_tracker.overflowed_ticks = 0;
+ }
}
safe_mode_t port_init(void) {
@@ -131,6 +149,21 @@ safe_mode_t port_init(void) {
analogin_init();
#endif
+ // If the board was reset by the WatchDogTimer, we may
+ // need to boot into safe mode. Reset the RESETREAS bit
+ // for the WatchDogTimer so we don't encounter this the
+ // next time we reboot.
+ if (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk) {
+ NRF_POWER->RESETREAS = POWER_RESETREAS_DOG_Msk;
+ uint32_t usb_reg = NRF_POWER->USBREGSTATUS;
+
+ // If USB is connected, then the user might be editing `code.py`,
+ // in which case we should reboot into Safe Mode.
+ if (usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk) {
+ return WATCHDOG_RESET;
+ }
+ }
+
return NO_SAFE_MODE;
}
@@ -174,6 +207,10 @@ void reset_port(void) {
bleio_reset();
#endif
+#if CIRCUITPY_WATCHDOG
+ watchdog_reset();
+#endif
+
reset_all_pins();
}
@@ -185,6 +222,10 @@ void reset_to_bootloader(void) {
}
void reset_cpu(void) {
+ // We're getting ready to reset, so save the counter off.
+ // This counter will get reset to zero during the reboot.
+ uint32_t ticks = nrfx_rtc_counter_get(&rtc_instance);
+ overflow_tracker.overflowed_ticks += ticks / 32;
NVIC_SystemReset();
}
@@ -228,7 +269,7 @@ uint64_t port_get_raw_ticks(uint8_t* subticks) {
if (subticks != NULL) {
*subticks = (rtc % 32);
}
- return overflowed_ticks + rtc / 32;
+ return overflow_tracker.overflowed_ticks + rtc / 32;
}
// Enable 1/1024 second tick.
diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk
index 46e8d43d3..67fbde6e4 100644
--- a/py/circuitpy_defns.mk
+++ b/py/circuitpy_defns.mk
@@ -240,6 +240,9 @@ endif
ifeq ($(CIRCUITPY_USTACK),1)
SRC_PATTERNS += ustack/%
endif
+ifeq ($(CIRCUITPY_WATCHDOG),1)
+SRC_PATTERNS += watchdog/%
+endif
ifeq ($(CIRCUITPY_PEW),1)
SRC_PATTERNS += _pew/%
endif
@@ -300,7 +303,10 @@ SRC_COMMON_HAL_ALL = \
rtc/RTC.c \
rtc/__init__.c \
supervisor/Runtime.c \
- supervisor/__init__.c
+ supervisor/__init__.c \
+ watchdog/__init__.c \
+ watchdog/WatchDogMode.c \
+ watchdog/WatchDogTimer.c \
SRC_COMMON_HAL = $(filter $(SRC_PATTERNS), $(SRC_COMMON_HAL_ALL))
diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h
index 62ef9062d..3fbb46900 100644
--- a/py/circuitpy_mpconfig.h
+++ b/py/circuitpy_mpconfig.h
@@ -632,6 +632,14 @@ extern const struct _mp_obj_module_t ustack_module;
#define RE_MODULE
#endif
+// This is not a top-level module; it's microcontroller.watchdog.
+#if CIRCUITPY_WATCHDOG
+extern const struct _mp_obj_module_t watchdog_module;
+#define WATCHDOG_MODULE { MP_ROM_QSTR(MP_QSTR_watchdog), MP_ROM_PTR(&watchdog_module) },
+#else
+#define WATCHDOG_MODULE
+#endif
+
// Define certain native modules with weak links so they can be replaced with Python
// implementations. This list may grow over time.
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
@@ -702,6 +710,7 @@ extern const struct _mp_obj_module_t ustack_module;
USB_HID_MODULE \
USB_MIDI_MODULE \
USTACK_MODULE \
+ WATCHDOG_MODULE \
// If weak links are enabled, just include strong links in the main list of modules,
// and also include the underscore alternate names.
diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk
index 0801540bc..865654a0d 100644
--- a/py/circuitpy_mpconfig.mk
+++ b/py/circuitpy_mpconfig.mk
@@ -228,6 +228,10 @@ CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART)
CIRCUITPY_ULAB ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_ULAB=$(CIRCUITPY_ULAB)
+# watchdog hardware support
+CIRCUITPY_WATCHDOG ?= 0
+CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG)
+
# Enabled micropython.native decorator (experimental)
CIRCUITPY_ENABLE_MPY_NATIVE ?= 0
CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE)
diff --git a/shared-bindings/microcontroller/__init__.c b/shared-bindings/microcontroller/__init__.c
index 21e1c90a9..88fe9c224 100644
--- a/shared-bindings/microcontroller/__init__.c
+++ b/shared-bindings/microcontroller/__init__.c
@@ -167,6 +167,11 @@ STATIC const mp_rom_map_elem_t mcu_module_globals_table[] = {
#else
{ MP_ROM_QSTR(MP_QSTR_nvm), MP_ROM_PTR(&mp_const_none_obj) },
#endif
+ #if CIRCUITPY_WATCHDOG
+ { MP_ROM_QSTR(MP_QSTR_watchdog), MP_ROM_PTR(&common_hal_mcu_watchdogtimer_obj) },
+ #else
+ { MP_ROM_QSTR(MP_QSTR_watchdog), MP_ROM_PTR(&mp_const_none_obj) },
+ #endif
{ MP_ROM_QSTR(MP_QSTR_RunMode), MP_ROM_PTR(&mcu_runmode_type) },
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&mcu_pin_type) },
{ MP_ROM_QSTR(MP_QSTR_pin), MP_ROM_PTR(&mcu_pin_module) },
diff --git a/shared-bindings/microcontroller/__init__.h b/shared-bindings/microcontroller/__init__.h
index e1487c555..8abdff763 100644
--- a/shared-bindings/microcontroller/__init__.h
+++ b/shared-bindings/microcontroller/__init__.h
@@ -49,10 +49,13 @@ extern const mcu_processor_obj_t common_hal_mcu_processor_obj;
#if CIRCUITPY_INTERNAL_NVM_SIZE > 0
-
#include "common-hal/nvm/ByteArray.h"
extern const nvm_bytearray_obj_t common_hal_mcu_nvm_obj;
+#endif
+#if CIRCUITPY_WATCHDOG
+#include "common-hal/watchdog/WatchDogTimer.h"
+extern watchdog_watchdogtimer_obj_t common_hal_mcu_watchdogtimer_obj;
#endif
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER___INIT___H
diff --git a/shared-bindings/watchdog/WatchDogMode.c b/shared-bindings/watchdog/WatchDogMode.c
new file mode 100644
index 000000000..369454c11
--- /dev/null
+++ b/shared-bindings/watchdog/WatchDogMode.c
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Sean Cross for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "shared-bindings/watchdog/WatchDogMode.h"
+
+//| class WatchDogMode:
+//| """run state of the watchdog timer"""
+//|
+//| def __init__(self, ):
+//| """Enum-like class to define the run mode of the watchdog timer."""
+//|
+//| RAISE: Any = ...
+//| """Raise an exception when the WatchDogTimer expires.
+//|
+//| :type watchdog.WatchDogMode:"""
+//|
+//| RESET: Any = ...
+//| """Reset the system if the WatchDogTimer expires.
+//|
+//| :type watchdog.WatchDogMode:"""
+//|
+const mp_obj_type_t watchdog_watchdogmode_type;
+
+const watchdog_watchdogmode_obj_t watchdog_watchdogmode_raise_obj = {
+ { &watchdog_watchdogmode_type },
+};
+
+const watchdog_watchdogmode_obj_t watchdog_watchdogmode_reset_obj = {
+ { &watchdog_watchdogmode_type },
+};
+
+watchdog_watchdogmode_t watchdog_watchdogmode_obj_to_type(mp_obj_t obj) {
+ if (obj == MP_ROM_PTR(&watchdog_watchdogmode_raise_obj)) {
+ return WATCHDOGMODE_RAISE;
+ } else if (obj == MP_ROM_PTR(&watchdog_watchdogmode_reset_obj)) {
+ return WATCHDOGMODE_RESET;
+ }
+ return WATCHDOGMODE_NONE;
+}
+
+mp_obj_t watchdog_watchdogmode_type_to_obj(watchdog_watchdogmode_t mode) {
+ switch (mode) {
+ case WATCHDOGMODE_RAISE:
+ return (mp_obj_t)MP_ROM_PTR(&watchdog_watchdogmode_raise_obj);
+ case WATCHDOGMODE_RESET:
+ return (mp_obj_t)MP_ROM_PTR(&watchdog_watchdogmode_reset_obj);
+ case WATCHDOGMODE_NONE:
+ default:
+ return (mp_obj_t)MP_ROM_PTR(&mp_const_none_obj);
+ }
+}
+
+STATIC const mp_rom_map_elem_t watchdog_watchdogmode_locals_dict_table[] = {
+ {MP_ROM_QSTR(MP_QSTR_RAISE), MP_ROM_PTR(&watchdog_watchdogmode_raise_obj)},
+ {MP_ROM_QSTR(MP_QSTR_RESET), MP_ROM_PTR(&watchdog_watchdogmode_reset_obj)},
+};
+STATIC MP_DEFINE_CONST_DICT(watchdog_watchdogmode_locals_dict, watchdog_watchdogmode_locals_dict_table);
+
+STATIC void watchdog_watchdogmode_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+ qstr runmode = MP_QSTR_None;
+ if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&watchdog_watchdogmode_raise_obj)) {
+ runmode = MP_QSTR_RAISE;
+ }
+ else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&watchdog_watchdogmode_reset_obj)) {
+ runmode = MP_QSTR_RESET;
+ }
+ mp_printf(print, "%q.%q.%q", MP_QSTR_watchdog, MP_QSTR_WatchDogMode,
+ runmode);
+}
+
+const mp_obj_type_t watchdog_watchdogmode_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_WatchDogMode,
+ .print = watchdog_watchdogmode_print,
+ .locals_dict = (mp_obj_t)&watchdog_watchdogmode_locals_dict,
+};
diff --git a/shared-bindings/watchdog/WatchDogMode.h b/shared-bindings/watchdog/WatchDogMode.h
new file mode 100644
index 000000000..68022671f
--- /dev/null
+++ b/shared-bindings/watchdog/WatchDogMode.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Sean Cross for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGMODE_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGMODE_H
+
+#include "py/obj.h"
+
+typedef enum {
+ WATCHDOGMODE_NONE,
+ WATCHDOGMODE_RAISE,
+ WATCHDOGMODE_RESET,
+} watchdog_watchdogmode_t;
+
+const mp_obj_type_t watchdog_watchdogmode_type;
+
+watchdog_watchdogmode_t watchdog_watchdogmode_obj_to_type(mp_obj_t obj);
+mp_obj_t watchdog_watchdogmode_type_to_obj(watchdog_watchdogmode_t mode);
+
+typedef struct {
+ mp_obj_base_t base;
+} watchdog_watchdogmode_obj_t;
+extern const watchdog_watchdogmode_obj_t watchdog_watchdogmode_raise_obj;
+extern const watchdog_watchdogmode_obj_t watchdog_watchdogmode_reset_obj;
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGMODE_H
diff --git a/shared-bindings/watchdog/WatchDogTimer.c b/shared-bindings/watchdog/WatchDogTimer.c
new file mode 100644
index 000000000..52bda4c77
--- /dev/null
+++ b/shared-bindings/watchdog/WatchDogTimer.c
@@ -0,0 +1,186 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Nick Moore for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include <math.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#include "common-hal/watchdog/WatchDogTimer.h"
+
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/watchdog/__init__.h"
+#include "shared-bindings/watchdog/WatchDogTimer.h"
+
+#include "supervisor/port.h"
+
+//| class WatchDogTimer:
+//| """Timer that is used to detect code lock ups and automatically reset the microcontroller
+//| when one is detected.
+//|
+//| A lock up is detected when the watchdog hasn't been fed after a given duration. So, make
+//| sure to call `feed` within the timeout.
+//| """
+//|
+
+//| def __init__(self, ):
+//| """Not currently dynamically supported. Access the sole instance through `microcontroller.watchdog`."""
+//| ...
+//|
+
+//| def feed(self):
+//| """Feed the watchdog timer. This must be called regularly, otherwise
+//| the timer will expire."""
+//| ...
+//|
+STATIC mp_obj_t watchdog_watchdogtimer_feed(mp_obj_t self_in) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ watchdog_watchdogmode_t current_mode = common_hal_watchdog_get_mode(self);
+
+ if (current_mode == WATCHDOGMODE_NONE) {
+ mp_raise_ValueError(translate("WatchDogTimer is not currently running"));
+ }
+ common_hal_watchdog_feed(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_feed_obj, watchdog_watchdogtimer_feed);
+
+//| def deinit(self):
+//| """Stop the watchdog timer. This may raise an error if the watchdog
+//| timer cannot be disabled on this platform."""
+//| ...
+//|
+STATIC mp_obj_t watchdog_watchdogtimer_deinit(mp_obj_t self_in) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ watchdog_watchdogmode_t current_mode = common_hal_watchdog_get_mode(self);
+
+ if (current_mode == WATCHDOGMODE_RESET) {
+ mp_raise_NotImplementedError(translate("WatchDogTimer cannot be deinitialized once mode is set to RESET"));
+ }
+
+ common_hal_watchdog_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_deinit_obj, watchdog_watchdogtimer_deinit);
+
+//| timeout: float = ...
+//| """The maximum number of seconds that can elapse between calls
+//| to feed()"""
+//|
+STATIC mp_obj_t watchdog_watchdogtimer_obj_get_timeout(mp_obj_t self_in) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ return mp_obj_new_float(common_hal_watchdog_get_timeout(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_get_timeout_obj, watchdog_watchdogtimer_obj_get_timeout);
+
+STATIC mp_obj_t watchdog_watchdogtimer_obj_set_timeout(mp_obj_t self_in, mp_obj_t timeout_obj) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_float_t timeout = mp_obj_get_float(timeout_obj);
+
+ if (timeout <= 0) {
+ mp_raise_ValueError(translate("watchdog timeout must be greater than 0"));
+ }
+
+ common_hal_watchdog_set_timeout(self, timeout);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(watchdog_watchdogtimer_set_timeout_obj, watchdog_watchdogtimer_obj_set_timeout);
+
+const mp_obj_property_t watchdog_watchdogtimer_timeout_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&watchdog_watchdogtimer_get_timeout_obj,
+ (mp_obj_t)&watchdog_watchdogtimer_set_timeout_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| mode: watchdog.WatchDogMode = ...
+//| """The current operating mode of the WatchDogTimer `watchdog.WatchDogMode`.
+//|
+//| Setting a WatchDogMode activates the WatchDog::
+//|
+//| import microcontroller
+//| import watchdog
+//|
+//| w = microcontroller.watchdog
+//| w.timeout = 5
+//| w.mode = watchdog.WatchDogMode.RAISE
+//|
+//|
+//| Once set, the WatchDogTimer will perform the specified action if the timer expires."""
+//|
+STATIC mp_obj_t watchdog_watchdogtimer_obj_get_mode(mp_obj_t self_in) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ return watchdog_watchdogmode_type_to_obj(common_hal_watchdog_get_mode(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_get_mode_obj, watchdog_watchdogtimer_obj_get_mode);
+
+STATIC mp_obj_t watchdog_watchdogtimer_obj_set_mode(mp_obj_t self_in, mp_obj_t mode_obj) {
+ watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ watchdog_watchdogmode_t current_mode = common_hal_watchdog_get_mode(self);
+ watchdog_watchdogmode_t new_mode = watchdog_watchdogmode_obj_to_type(mode_obj);
+ mp_float_t current_timeout = common_hal_watchdog_get_timeout(self);
+
+ // When setting the mode, the timeout value must be greater than zero
+ if (new_mode == WATCHDOGMODE_RESET || new_mode == WATCHDOGMODE_RAISE) {
+ if (current_timeout <= 0) {
+ mp_raise_ValueError(translate("WatchDogTimer.timeout must be greater than 0"));
+ }
+ }
+
+ // Don't allow changing the mode once the watchdog timer has been started
+ if (current_mode == WATCHDOGMODE_RESET && new_mode != WATCHDOGMODE_RESET) {
+ mp_raise_TypeError(translate("WatchDogTimer.mode cannot be changed once set to WatchDogMode.RESET"));
+ }
+
+ common_hal_watchdog_set_mode(self, new_mode);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(watchdog_watchdogtimer_set_mode_obj, watchdog_watchdogtimer_obj_set_mode);
+
+const mp_obj_property_t watchdog_watchdogtimer_mode_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&watchdog_watchdogtimer_get_mode_obj,
+ (mp_obj_t)&watchdog_watchdogtimer_set_mode_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+STATIC const mp_rom_map_elem_t watchdog_watchdogtimer_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_feed), MP_ROM_PTR(&watchdog_watchdogtimer_feed_obj) },
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&watchdog_watchdogtimer_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&watchdog_watchdogtimer_timeout_obj) },
+ { MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&watchdog_watchdogtimer_mode_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(watchdog_watchdogtimer_locals_dict, watchdog_watchdogtimer_locals_dict_table);
+
+const mp_obj_type_t watchdog_watchdogtimer_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_WatchDogTimer,
+ // .make_new = watchdog_watchdogtimer_make_new,
+ .locals_dict = (mp_obj_dict_t*)&watchdog_watchdogtimer_locals_dict,
+};
diff --git a/shared-bindings/watchdog/WatchDogTimer.h b/shared-bindings/watchdog/WatchDogTimer.h
new file mode 100644
index 000000000..48044748a
--- /dev/null
+++ b/shared-bindings/watchdog/WatchDogTimer.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Noralf Trønnes
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGTIMER_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGTIMER_H
+
+#include <py/obj.h>
+#include "shared-bindings/watchdog/WatchDogMode.h"
+
+typedef struct _watchdog_watchdogtimer_obj_t watchdog_watchdogtimer_obj_t;
+
+extern void common_hal_watchdog_feed(watchdog_watchdogtimer_obj_t *self);
+
+extern void common_hal_watchdog_set_mode(watchdog_watchdogtimer_obj_t *self, watchdog_watchdogmode_t);
+extern watchdog_watchdogmode_t common_hal_watchdog_get_mode(watchdog_watchdogtimer_obj_t *self);
+
+extern void common_hal_watchdog_set_timeout(watchdog_watchdogtimer_obj_t *self, mp_float_t timeout);
+extern mp_float_t common_hal_watchdog_get_timeout(watchdog_watchdogtimer_obj_t *self);
+
+extern void common_hal_watchdog_enable(watchdog_watchdogtimer_obj_t *self);
+extern void common_hal_watchdog_deinit(watchdog_watchdogtimer_obj_t *self);
+
+extern const mp_obj_type_t watchdog_watchdogtimer_type;
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGTIMER_H
diff --git a/shared-bindings/watchdog/__init__.c b/shared-bindings/watchdog/__init__.c
new file mode 100644
index 000000000..76e631729
--- /dev/null
+++ b/shared-bindings/watchdog/__init__.c
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "py/runtime.h"
+#include "shared-bindings/watchdog/__init__.h"
+#include "shared-bindings/watchdog/WatchDogMode.h"
+
+//| """Watchdog Timer
+//|
+//| The `watchdog` module provides support for a Watchdog Timer. This timer will reset the device
+//| if it hasn't been fed after a specified amount of time. This is useful to ensure the board
+//| has not crashed or locked up. Note that on some platforms the watchdog timer cannot be disabled
+//| once it has been enabled.
+//|
+//| The `WatchDogTimer` is used to restart the system when the application crashes and ends
+//| up into a non recoverable state. Once started it cannot be stopped or
+//| reconfigured in any way. After enabling, the application must "feed" the
+//| watchdog periodically to prevent it from expiring and resetting the system.
+//|
+//| Example usage::
+//|
+//| from microcontroller import watchdog as w
+//| from watchdog import WatchDogMode
+//| w.timeout=2.5 # Set a timeout of 2.5 seconds
+//| w.mode = WatchDogMode.RAISE
+//| w.feed()"""
+//|
+
+const mp_obj_type_t mp_type_WatchDogTimeout = {
+ { &mp_type_type },
+ .name = MP_QSTR_WatchDogTimeout,
+ .make_new = mp_obj_exception_make_new,
+ .attr = mp_obj_exception_attr,
+ .parent = &mp_type_Exception,
+};
+
+mp_obj_exception_t mp_watchdog_timeout_exception = {
+ .base.type = &mp_type_WatchDogTimeout,
+ .traceback_alloc = 0,
+ .traceback_len = 0,
+ .traceback_data = NULL,
+ .args = (mp_obj_tuple_t*)&mp_const_empty_tuple_obj,
+};
+
+STATIC const mp_rom_map_elem_t watchdog_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_watchdog) },
+ { MP_ROM_QSTR(MP_QSTR_WatchDogMode), MP_ROM_PTR(&watchdog_watchdogmode_type) },
+ { MP_ROM_QSTR(MP_QSTR_WatchDogTimeout), MP_ROM_PTR(&mp_type_WatchDogTimeout) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(watchdog_module_globals, watchdog_module_globals_table);
+
+const mp_obj_module_t watchdog_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&watchdog_module_globals,
+};
diff --git a/shared-bindings/watchdog/__init__.h b/shared-bindings/watchdog/__init__.h
new file mode 100644
index 000000000..b5a0ad71d
--- /dev/null
+++ b/shared-bindings/watchdog/__init__.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG___INIT___H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG___INIT___H
+
+extern const mp_obj_module_t watchdog_module;
+extern mp_obj_exception_t mp_watchdog_timeout_exception;
+extern const mp_obj_type_t mp_type_WatchDogTimeout;
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG___INIT___H
diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c
index c957aee53..a167ab392 100644
--- a/supervisor/shared/safe_mode.c
+++ b/supervisor/shared/safe_mode.c
@@ -160,6 +160,9 @@ void print_safe_mode_message(safe_mode_t reason) {
case MEM_MANAGE:
serial_write_compressed(translate("Invalid memory access."));
break;
+ case WATCHDOG_RESET:
+ serial_write_compressed(translate("Watchdog timer expired."));
+ break;
default:
serial_write_compressed(translate("Unknown reason."));
break;
diff --git a/supervisor/shared/safe_mode.h b/supervisor/shared/safe_mode.h
index 5b09c4b54..c160739ae 100644
--- a/supervisor/shared/safe_mode.h
+++ b/supervisor/shared/safe_mode.h
@@ -41,6 +41,7 @@ typedef enum {
NORDIC_SOFT_DEVICE_ASSERT,
FLASH_WRITE_FAIL,
MEM_MANAGE,
+ WATCHDOG_RESET,
} safe_mode_t;
safe_mode_t wait_for_safe_mode_reset(void);
diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c
index dc38e76f6..23516799f 100644
--- a/supervisor/shared/tick.c
+++ b/supervisor/shared/tick.c
@@ -44,6 +44,13 @@ static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
#include "shared-bindings/microcontroller/__init__.h"
+#if CIRCUITPY_WATCHDOG
+#include "shared-bindings/watchdog/__init__.h"
+#define WATCHDOG_EXCEPTION_CHECK() (MP_STATE_VM(mp_pending_exception) == &mp_watchdog_timeout_exception)
+#else
+#define WATCHDOG_EXCEPTION_CHECK() 0
+#endif
+
void supervisor_tick(void) {
#if CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS > 0
filesystem_tick();
@@ -96,7 +103,8 @@ void mp_hal_delay_ms(mp_uint_t delay) {
RUN_BACKGROUND_TASKS;
// Check to see if we've been CTRL-Ced by autoreload or the user.
if(MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)) ||
- MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) {
+ MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception)) ||
+ WATCHDOG_EXCEPTION_CHECK()) {
break;
}
remaining = end_tick - port_get_raw_ticks(NULL);