From b10182bbcc0ee78ce5d12902bc8da68bdc4429c5 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Mon, 1 Jan 2024 18:21:39 +0100 Subject: [PATCH] nrf: Fix non-running LFCLK. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under some circumstances, after a hard reset, the low-frequency clock would not be running. This caused time.ticks_ms() to return 0, time.sleep_ms() to get stuck, and other misbehavior. A soft reboot would return it to a working state. The cause was a race condition that was hit when the bootloader would itself turn LFCLK on, but turn it off again shortly before launching the main application (this apparently happens with the Adafruit bootloader from https://github.com/fanoush/ds-d6/tree/master/micropython). Stopping the clock is an asynchronous operation and it continues running for a short time after the stop command is given. When MicroPython checked whether to start it by looking at the LFCLKSTAT register (nrf_clock_lf_is_running) during that time, it would mistakenly not be started again. What MicroPython should be looking at is not whether the clock is running at this time, but whether a start/stop command has been given, which is indicated by the LFCLKRUN register (nrf_clock_lf_start_task_status_get). It is not clearly documented, but empirically LFCLKRUN is not just set when the LFCLKSTART task is triggered, but also cleared when the LFCLKSTOP task is triggered, which is exactly what we need. The matter is complicated by the fact that the nRF52832 has an anomaly (see [errata](https://infocenter.nordicsemi.com/topic/errata_nRF52832_Rev3/ERR/nRF52832/Rev3/latest/anomaly_832_132.html?cp=5_2_1_0_1_33)) where starting the LFCLK will not work between 66µs and 138µs after it last stopped. Apply a workaround for that. See nrfx_clock_lfclk_start() in micropython/lib/nrfx/drivers/src/nrfx_clock.c for reference, but we are not using that because it also does other things and makes the code larger. Signed-off-by: Christian Walther --- ports/nrf/modules/machine/rtcounter.c | 5 ++-- ports/nrf/mphalport.c | 34 ++++++++++++++++++++++++--- ports/nrf/mphalport.h | 2 ++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/ports/nrf/modules/machine/rtcounter.c b/ports/nrf/modules/machine/rtcounter.c index 08d31f61d0..c1ef1b4dd9 100644 --- a/ports/nrf/modules/machine/rtcounter.c +++ b/ports/nrf/modules/machine/rtcounter.c @@ -27,6 +27,7 @@ #include "py/nlr.h" #include "py/runtime.h" +#include "mphalport.h" #include "rtcounter.h" #include "nrfx_rtc.h" #include "nrf_clock.h" @@ -182,9 +183,7 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s } // Start the low-frequency clock (if it hasn't been started already) - if (!nrf_clock_lf_is_running(NRF_CLOCK)) { - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); - } + mp_nrf_start_lfclk(); // Make sure it's uninitialized. nrfx_rtc_uninit(self->p_rtc); diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 2a1a4cb13b..06c6ba5cc2 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -40,6 +40,36 @@ #include "nrf_clock.h" #endif +#if !defined(USE_WORKAROUND_FOR_ANOMALY_132) && \ + (defined(NRF52832_XXAA) || defined(NRF52832_XXAB)) +// ANOMALY 132 - LFCLK needs to avoid frame from 66us to 138us after LFCLK stop. +#define USE_WORKAROUND_FOR_ANOMALY_132 1 +#endif + +#if USE_WORKAROUND_FOR_ANOMALY_132 +#include "soc/nrfx_coredep.h" +#endif + +void mp_nrf_start_lfclk(void) { + if (!nrf_clock_lf_start_task_status_get(NRF_CLOCK)) { + // Check if the clock was recently stopped but is still running. + #if USE_WORKAROUND_FOR_ANOMALY_132 + bool was_running = nrf_clock_lf_is_running(NRF_CLOCK); + // If so, wait for it to stop. This ensures that the delay for anomaly 132 workaround does + // not land us in the middle of the forbidden interval. + while (nrf_clock_lf_is_running(NRF_CLOCK)) { + } + // If the clock just stopped, we can start it again right away as we are certainly before + // the forbidden 66-138us interval. Otherwise, apply a delay of 138us to make sure we are + // after the interval. + if (!was_running) { + nrfx_coredep_delay_us(138); + } + #endif + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); + } +} + #if MICROPY_PY_TIME_TICKS // Use RTC1 for time ticks generation (ms and us) with 32kHz tick resolution @@ -99,9 +129,7 @@ static void rtc_irq_time(nrfx_rtc_int_type_t event) { void rtc1_init_time_ticks(void) { // Start the low-frequency clock (if it hasn't been started already) - if (!nrf_clock_lf_is_running(NRF_CLOCK)) { - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); - } + mp_nrf_start_lfclk(); // Uninitialize first, then set overflow IRQ and first CC event nrfx_rtc_uninit(&rtc1); nrfx_rtc_init(&rtc1, &rtc_config_time_ticks, rtc_irq_time); diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index 1ba4b6e70f..7efe05a15f 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -54,6 +54,8 @@ void mp_hal_delay_us(mp_uint_t us); const char *nrfx_error_code_lookup(uint32_t err_code); +void mp_nrf_start_lfclk(void); + #define MP_HAL_PIN_FMT "%q" #define mp_hal_pin_obj_t const pin_obj_t * #define mp_hal_get_pin_obj(o) pin_find(o)