]> granicus.if.org Git - esp-idf/commitdiff
Add support for 32k XTAL as RTC_SLOW_CLK source
authorIvan Grokhotkov <ivan@espressif.com>
Mon, 24 Apr 2017 10:36:47 +0000 (18:36 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Wed, 26 Apr 2017 04:43:22 +0000 (12:43 +0800)
- RTC_CNTL_SLOWCLK_FREQ define is removed; rtc_clk_slow_freq_get_hz
  function can be used instead to get an approximate RTC_SLOW_CLK
  frequency

- Clock calibration is performed at startup. The value is saved and used
  for timekeeping and when entering deep sleep.

- When using the 32k XTAL, startup code will wait for the oscillator to
  start up. This can be possibly optimized by starting a separate task
  to wait for oscillator startup, and performing clock switch in that
  task.

- Fix a bug that 32k XTAL would be disabled in rtc_clk_init.

- Fix a rounding error in rtc_clk_cal, which caused systematic frequency
  error.

- Fix an overflow bug which caused rtc_clk_cal to timeout early if the
  slow_clk_cycles argument would exceed certain value

- Improve 32k XTAL oscillator startup time by introducing bootstrapping
  code, which uses internal pullup/pulldown resistors on 32K_N/32K_P
  pins to set better initial conditions for the oscillator.

16 files changed:
components/bootloader/src/main/bootloader_start.c
components/esp32/Kconfig
components/esp32/clk.c [new file with mode: 0644]
components/esp32/cpu_freq.c [deleted file]
components/esp32/cpu_start.c
components/esp32/deep_sleep.c
components/esp32/include/esp_clk.h [new file with mode: 0644]
components/esp32/panic.c
components/newlib/time.c
components/soc/esp32/include/soc/cpu.h
components/soc/esp32/include/soc/rtc.h
components/soc/esp32/include/soc/rtc_cntl_reg.h
components/soc/esp32/rtc_clk.c
components/soc/esp32/rtc_time.c
components/ulp/test/test_ulp.c
examples/system/deep_sleep/main/deep_sleep_example_main.c

index aa30ad4c74dad2ecf1d758ebc757ec28e5d1fba3..5a540d5d1bdb3febd7af1be80d139c592d10abf5 100644 (file)
@@ -59,8 +59,6 @@ We arrive here after the bootloader finished loading the program from flash. The
 flash cache is down and the app CPU is in reset. We do have a stack, so we can do the initialization in C.
 */
 
-// TODO: make a nice header file for ROM functions instead of adding externs all over the place
-extern void Cache_Flush(int);
 
 void bootloader_main();
 static void unpack_load_app(const esp_partition_pos_t *app_node);
@@ -73,6 +71,7 @@ static void set_cache_and_start_app(uint32_t drom_addr,
     uint32_t irom_size,
     uint32_t entry_addr);
 static void update_flash_config(const esp_image_header_t* pfhdr);
+static void clock_configure(void);
 static void uart_console_configure(void);
 static void wdt_reset_check(void);
 
@@ -237,15 +236,7 @@ static bool ota_select_valid(const esp_ota_select_entry_t *s)
 
 void bootloader_main()
 {
-    /* Set CPU to 80MHz. Keep other clocks unmodified. */
-    uart_tx_wait_idle(0);
-    rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
-    clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
-    clk_cfg.cpu_freq = RTC_CPU_FREQ_80M;
-    clk_cfg.slow_freq = rtc_clk_slow_freq_get();
-    clk_cfg.fast_freq = rtc_clk_fast_freq_get();
-    rtc_clk_init(clk_cfg);
-
+    clock_configure();
     uart_console_configure();
     wdt_reset_check();
     ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER);
@@ -696,6 +687,29 @@ void print_flash_info(const esp_image_header_t* phdr)
 #endif
 }
 
+
+static void clock_configure(void)
+{
+    /* Set CPU to 80MHz. Keep other clocks unmodified. */
+    uart_tx_wait_idle(0);
+    rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
+    clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
+    clk_cfg.cpu_freq = RTC_CPU_FREQ_80M;
+    clk_cfg.slow_freq = rtc_clk_slow_freq_get();
+    clk_cfg.fast_freq = rtc_clk_fast_freq_get();
+    rtc_clk_init(clk_cfg);
+    /* As a slight optimization, if 32k XTAL was enabled in sdkconfig, we enable
+     * it here. Usually it needs some time to start up, so we amortize at least
+     * part of the start up time by enabling 32k XTAL early.
+     * App startup code will wait until the oscillator has started up.
+     */
+#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
+    if (!rtc_clk_32k_enabled()) {
+        rtc_clk_32k_bootstrap();
+    }
+#endif
+}
+
 static void uart_console_configure(void)
 {
 #if CONFIG_CONSOLE_UART_NONE
index 6a73463c58971a81131510e4c5c456465ed3340f..f9b9151597e84411542ce2b53ea33b44c044d6d6 100644 (file)
@@ -542,6 +542,23 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
     bool "External 32kHz crystal"
 endchoice
 
+config ESP32_RTC_CLK_CAL_CYCLES
+    int "Number of cycles for RTC_SLOW_CLK calibration"
+    default 1024
+    range 0 125000
+    help
+        When the startup code initializes RTC_SLOW_CLK, it can perform
+        calibration by comparing the RTC_SLOW_CLK frequency with main XTAL
+        frequency. This option sets the number of RTC_SLOW_CLK cycles measured
+        by the calibration routine. Higher numbers increase calibration
+        precision, which may be important for applications which spend a lot of
+        time in deep sleep. Lower numbers reduce startup time.
+        
+        When this option is set to 0, clock calibration will not be performed at
+        startup, and approximate clock frequencies will be assumed:
+        - 150000 Hz if internal RC oscillator is used as clock source
+        - 32768 Hz if the 32k crystal oscillator is used
+
 config ESP32_DEEP_SLEEP_WAKEUP_DELAY
     int "Extra delay in deep sleep wake stub (in us)"
     default 0
diff --git a/components/esp32/clk.c b/components/esp32/clk.c
new file mode 100644 (file)
index 0000000..449ea39
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include "sdkconfig.h"
+#include "esp_attr.h"
+#include "esp_log.h"
+#include "rom/ets_sys.h"
+#include "rom/uart.h"
+#include "soc/soc.h"
+#include "soc/rtc.h"
+#include "soc/rtc_cntl_reg.h"
+
+/* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
+ * Larger values increase startup delay. Smaller values may cause false positive
+ * detection (i.e. oscillator runs for a few cycles and then stops).
+ */
+#define XTAL_32K_DETECT_CYCLES  32
+#define SLOW_CLK_CAL_CYCLES     CONFIG_ESP32_RTC_CLK_CAL_CYCLES
+
+static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk);
+
+static const char* TAG = "clk";
+/*
+ * This function is not exposed as an API at this point,
+ * because FreeRTOS doesn't yet support dynamic changing of
+ * CPU frequency. Also we need to implement hooks for
+ * components which want to be notified of CPU frequency
+ * changes.
+ */
+void esp_clk_init(void)
+{
+    rtc_config_t cfg = RTC_CONFIG_DEFAULT();
+    rtc_init(cfg);
+
+#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
+    select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL);
+#else
+    select_rtc_slow_clk(RTC_SLOW_FREQ_RTC);
+#endif
+
+    uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
+    rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M;
+    switch(freq_mhz) {
+        case 240:
+            freq = RTC_CPU_FREQ_240M;
+            break;
+        case 160:
+            freq = RTC_CPU_FREQ_160M;
+            break;
+        default:
+            freq_mhz = 80;
+            /* no break */
+        case 80:
+            freq = RTC_CPU_FREQ_80M;
+            break;
+    }
+
+    // Wait for UART TX to finish, otherwise some UART output will be lost
+    // when switching APB frequency
+    uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
+    rtc_clk_cpu_freq_set(freq);
+}
+
+void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
+{
+    extern uint32_t g_ticks_per_us_pro;  // g_ticks_us defined in ROM for PRO CPU
+    extern uint32_t g_ticks_per_us_app;  // same defined for APP CPU
+    g_ticks_per_us_pro = ticks_per_us;
+    g_ticks_per_us_app = ticks_per_us;
+}
+
+/* This is a cached value of RTC slow clock period; it is updated by
+ * the select_rtc_slow_clk function at start up. This cached value is used in
+ * other places, like time syscalls and deep sleep.
+ */
+static uint32_t s_rtc_slow_clk_cal = 0;
+
+static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
+{
+    if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
+        /* 32k XTAL oscillator needs to be enabled and running before it can
+         * be used. Hardware doesn't have a direct way of checking if the
+         * oscillator is running. Here we use rtc_clk_cal function to count
+         * the number of main XTAL cycles in the given number of 32k XTAL
+         * oscillator cycles. If the 32k XTAL has not started up, calibration
+         * will time out, returning 0.
+         */
+        rtc_clk_32k_enable(true);
+        uint32_t cal_val = 0;
+        uint32_t wait = 0;
+        // increment of 'wait' counter equivalent to 3 seconds
+        const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * XTAL_32K_DETECT_CYCLES);
+        ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up")
+        do {
+            ++wait;
+            cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, XTAL_32K_DETECT_CYCLES);
+            if (wait % warning_timeout == 0) {
+                ESP_EARLY_LOGW(TAG, "still waiting for 32k oscillator to start up");
+            }
+        } while (cal_val == 0);
+        ESP_EARLY_LOGD(TAG, "32k oscillator ready, wait=%d", wait);
+    }
+    rtc_clk_slow_freq_set(slow_clk);
+    if (SLOW_CLK_CAL_CYCLES > 0) {
+        /* TODO: 32k XTAL oscillator has some frequency drift at startup.
+         * Improve calibration routine to wait until the frequency is stable.
+         */
+        s_rtc_slow_clk_cal = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
+    } else {
+        const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
+        s_rtc_slow_clk_cal = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
+    }
+    ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", s_rtc_slow_clk_cal);
+}
+
+uint32_t esp_clk_slowclk_cal_get()
+{
+    return s_rtc_slow_clk_cal;
+}
diff --git a/components/esp32/cpu_freq.c b/components/esp32/cpu_freq.c
deleted file mode 100644 (file)
index 4efa825..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stdint.h>
-#include "esp_attr.h"
-#include "rom/ets_sys.h"
-#include "rom/uart.h"
-#include "sdkconfig.h"
-#include "soc/soc.h"
-#include "soc/rtc.h"
-#include "soc/rtc_cntl_reg.h"
-
-/*
- * This function is not exposed as an API at this point,
- * because FreeRTOS doesn't yet support dynamic changing of
- * CPU frequency. Also we need to implement hooks for
- * components which want to be notified of CPU frequency
- * changes.
- */
-void esp_set_cpu_freq(void)
-{
-    uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
-    rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M;
-    switch(freq_mhz) {
-        case 240:
-            freq = RTC_CPU_FREQ_240M;
-            break;
-        case 160:
-            freq = RTC_CPU_FREQ_160M;
-            break;
-        default:
-            freq_mhz = 80;
-            /* no break */
-        case 80:
-            freq = RTC_CPU_FREQ_80M;
-            break;
-    }
-
-    // Wait for UART TX to finish, otherwise some UART output will be lost
-    // when switching APB frequency
-    uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
-    rtc_config_t cfg = RTC_CONFIG_DEFAULT();
-    rtc_init(cfg);
-    rtc_clk_cpu_freq_set(freq);
-#if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
-    rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
-#endif
-}
-
-void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
-{
-    extern uint32_t g_ticks_per_us_pro;  // g_ticks_us defined in ROM for PRO CPU
-    extern uint32_t g_ticks_per_us_app;  // same defined for APP CPU
-    g_ticks_per_us_pro = ticks_per_us;
-    g_ticks_per_us_app = ticks_per_us;
-}
index 7211b8b479aa04735a4f54c5ca8097178aabe252..8ff0a7c8db9d0a36fff1eab03249e67ef2889854 100644 (file)
@@ -1,9 +1,9 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
-
+//
 //     http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
@@ -11,6 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+
 #include <stdint.h>
 #include <string.h>
 
@@ -60,6 +61,7 @@
 #include "esp_panic.h"
 #include "esp_core_dump.h"
 #include "esp_app_trace.h"
+#include "esp_clk.h"
 #include "trax.h"
 
 #define STRINGIFY(s) STRINGIFY2(s)
@@ -202,7 +204,7 @@ void start_cpu0_default(void)
 #endif
     trax_start_trace(TRAX_DOWNCOUNT_WORDS);
 #endif
-    esp_set_cpu_freq();     // set CPU frequency configured in menuconfig
+    esp_clk_init();
 #ifndef CONFIG_CONSOLE_UART_NONE
     uart_div_modify(CONFIG_CONSOLE_UART_NUM, (rtc_clk_apb_freq_get() << 4) / CONFIG_CONSOLE_UART_BAUDRATE);
 #endif
index 23d3f4b1fc783970c3061890e2b2e96a327ba71f..6f7e3b24c7fd6bebe1b98752e22051fbfeb7f119 100644 (file)
@@ -17,6 +17,7 @@
 #include "esp_attr.h"
 #include "esp_deep_sleep.h"
 #include "esp_log.h"
+#include "esp_clk.h"
 #include "rom/cache.h"
 #include "rom/rtc.h"
 #include "rom/uart.h"
@@ -183,13 +184,7 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us)
 
 static void timer_wakeup_prepare()
 {
-    // Do calibration if not using 32k XTAL
-    uint32_t period;
-    if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
-        period = rtc_clk_cal(RTC_CAL_RTC_MUX, 128);
-    } else {
-        period = (uint32_t) ((1000000ULL /* us*Hz */ << RTC_CLK_CAL_FRACT) / 32768 /* Hz */);
-    }
+    uint32_t period = esp_clk_slowclk_cal_get();
     uint64_t rtc_count_delta = rtc_time_us_to_slowclk(s_config.sleep_duration, period);
     uint64_t cur_rtc_count = rtc_time_get();
     rtc_sleep_set_wakeup_time(cur_rtc_count + rtc_count_delta);
diff --git a/components/esp32/include/esp_clk.h b/components/esp32/include/esp_clk.h
new file mode 100644 (file)
index 0000000..0cedf03
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+/**
+ * @file esp_clk.h
+ *
+ * This file contains declarations of clock related functions.
+ * These functions are used in ESP-IDF components, but should not be considered
+ * to be part of public API.
+ */
+
+/**
+ * @brief Initialize clock-related settings
+ *
+ * Called from cpu_start.c, not intended to be called from other places.
+ * This function configures the CPU clock, RTC slow and fast clocks, and
+ * performs RTC slow clock calibration.
+ */
+void esp_clk_init(void);
+
+
+/**
+ * @brief Get the cached calibration value of RTC slow clock
+ *
+ * The value is in the same format as returned by rtc_clk_cal (microseconds,
+ * in Q13.19 fixed-point format).
+ *
+ * @return the calibration value obtained using rtc_clk_cal, at startup time
+ */
+uint32_t esp_clk_slowclk_cal_get();
+
index 8ec362f9cd2b9a4074986f33f0b56de22d4b3fe5..5cedfcabb3139bbe8c1839db234ea851b58a222b 100644 (file)
@@ -317,7 +317,7 @@ static void esp_panic_wdt_start()
     REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_SYSTEM);
     // 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
     // @ 115200 UART speed it will take more than 6 sec to print them out.
-    WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, RTC_CNTL_SLOWCLK_FREQ*7);
+    WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 7);
     REG_SET_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN);
     WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0);
 }
index fc7dec6450dda6bed8784d9c86bb9ed1a0e954fe..d763f839041b797d8534482171283f82fe9ecabd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -24,7 +24,9 @@
 #include <rom/rtc.h>
 #include "esp_attr.h"
 #include "esp_intr_alloc.h"
+#include "esp_clk.h"
 #include "soc/soc.h"
+#include "soc/rtc.h"
 #include "soc/rtc_cntl_reg.h"
 #include "soc/frc_timer_reg.h"
 #include "rom/ets_sys.h"
 #ifdef WITH_RTC
 static uint64_t get_rtc_time_us()
 {
-    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M);
-    while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) {
-        ;
-    }
-    CLEAR_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M);
-    uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
-    uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG);
-    uint64_t ticks = (high << 32) | low;
-    return ticks * 100 / (RTC_CNTL_SLOWCLK_FREQ / 10000);    // scale RTC_CNTL_SLOWCLK_FREQ to avoid overflow
+    uint64_t ticks = rtc_time_get();
+    return (uint32_t) ((ticks * esp_clk_slowclk_cal_get()) >> RTC_CLK_CAL_FRACT);
 }
 #endif // WITH_RTC
 
index 7e1301345b30fcf8f493f1b51f802732262f0cd7..b56fb3dc84042f032986324365427d8986329d9e 100644 (file)
@@ -73,15 +73,6 @@ static inline void cpu_configure_region_protection()
     cpu_write_itlb(0x20000000, 0);
 }
 
-/**
- * @brief Set CPU frequency to the value defined in menuconfig
- *
- * Called from cpu_start.c, not intended to be called from other places.
- * This is a temporary function which will be replaced once dynamic
- * CPU frequency changing is implemented.
- */
-void esp_set_cpu_freq(void);
-
 /**
  * @brief Stall CPU using RTC controller
  * @param cpu_id ID of the CPU to stall (0 = PRO, 1 = APP)
index a0813294aad2eadde336660bbf4856c4793e00fc..a85265fdc1796c55d62380e4cb120f358ea6ad03 100644 (file)
@@ -168,6 +168,15 @@ void rtc_clk_32k_enable(bool en);
  */
 bool rtc_clk_32k_enabled();
 
+/**
+ * @brief Enable 32k oscillator, configuring it for fast startup time.
+ * Note: to achieve higher frequency stability, rtc_clk_32k_enable function
+ * must be called one the 32k XTAL oscillator has started up. This function
+ * will initially disable the 32k XTAL oscillator, so it should not be called
+ * when the system is using 32k XTAL as RTC_SLOW_CLK.
+ */
+void rtc_clk_32k_bootstrap();
+
 /**
  * @brief Enable or disable 8 MHz internal oscillator
  *
@@ -229,6 +238,20 @@ void rtc_clk_slow_freq_set(rtc_slow_freq_t slow_freq);
  */
 rtc_slow_freq_t rtc_clk_slow_freq_get();
 
+/**
+ * @brief Get the approximate frequency of RTC_SLOW_CLK, in Hz
+ *
+ * - if RTC_SLOW_FREQ_RTC is selected, returns ~150000
+ * - if RTC_SLOW_FREQ_32K_XTAL is selected, returns 32768
+ * - if RTC_SLOW_FREQ_8MD256 is selected, returns ~33000
+ *
+ * rtc_clk_cal function can be used to get more precise value by comparing
+ * RTC_SLOW_CLK frequency to the frequency of main XTAL.
+ *
+ * @return RTC_SLOW_CLK frequency, in Hz
+ */
+uint32_t rtc_clk_slow_freq_get_hz();
+
 /**
  * @brief Select source for RTC_FAST_CLK
  * @param fast_freq clock source (one of rtc_fast_freq_t values)
index 067f2296fe6d64075e001a2d368df3f71c5c4b5f..efb53ec14713166e1cf1745272afc8a58ee25c1f 100644 (file)
 #define RTC_CNTL_TIME_VALID_V  0x1
 #define RTC_CNTL_TIME_VALID_S  30
 
-/* frequency of RTC slow clock, Hz */
-#define RTC_CNTL_SLOWCLK_FREQ   150000
-
 #define RTC_CNTL_TIME0_REG          (DR_REG_RTCCNTL_BASE + 0x10)
 /* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */
 /*description: RTC timer low 32 bits*/
index 1397705c21eb7379e2c6cf10f40d9b707ebd2a16..676b9f33cebfa78c45db511033b522a41f3f7d8e 100644 (file)
 
 #define MHZ (1000000)
 
+/* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */
+#define RTC_FAST_CLK_FREQ_8M        8500000
+#define RTC_SLOW_CLK_FREQ_150K      150000
+#define RTC_SLOW_CLK_FREQ_8MD256    (RTC_FAST_CLK_FREQ_8M / 256)
+#define RTC_SLOW_CLK_FREQ_32K       32768
+
 static const char* TAG = "rtc_clk";
 
 /* Various constants related to the analog internals of the chip.
@@ -55,14 +61,21 @@ static const char* TAG = "rtc_clk";
 #define XTAL_32K_DRES_VAL   3
 #define XTAL_32K_DBIAS_VAL  0
 
+#define XTAL_32K_BOOTSTRAP_DAC_VAL      3
+#define XTAL_32K_BOOTSTRAP_DRES_VAL     3
+#define XTAL_32K_BOOTSTRAP_DBIAS_VAL    0
+#define XTAL_32K_BOOTSTRAP_TIME_US      7
+
 /* Delays for various clock sources to be enabled/switched.
  * All values are in microseconds.
  * TODO: some of these are excessive, and should be reduced.
  */
-#define DELAY_CPU_FREQ_SWITCH_TO_XTAL   80
+#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K  80
+#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K   160
 #define DELAY_CPU_FREQ_SWITCH_TO_PLL    10
 #define DELAY_PLL_DBIAS_RAISE           3
-#define DELAY_PLL_ENABLE                80
+#define DELAY_PLL_ENABLE_WITH_150K      80
+#define DELAY_PLL_ENABLE_WITH_32K       160
 #define DELAY_FAST_CLK_SWITCH           3
 #define DELAY_SLOW_CLK_SWITCH           300
 #define DELAY_8M_ENABLE                 50
@@ -73,22 +86,36 @@ static const char* TAG = "rtc_clk";
 #define XTAL_FREQ_EST_CYCLES            10
 
 
+static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias)
+{
+    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
+    CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
+            RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
+            RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
+    REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, dac);
+    REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, dres);
+    REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, dbias);
+    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
+}
+
 void rtc_clk_32k_enable(bool enable)
 {
     if (enable) {
-        SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
-        CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
-                RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
-                RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
-        REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, XTAL_32K_DAC_VAL);
-        REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, XTAL_32K_DRES_VAL);
-        REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, XTAL_32K_DBIAS_VAL);
-        SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
+        rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL);
     } else {
         CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
     }
 }
 
+void rtc_clk_32k_bootstrap()
+{
+    CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
+    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE);
+    ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US);
+    rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL,
+            XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL);
+}
+
 bool rtc_clk_32k_enabled()
 {
     return GET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K) != 0;
@@ -172,6 +199,15 @@ rtc_slow_freq_t rtc_clk_slow_freq_get()
     return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL);
 }
 
+uint32_t rtc_clk_slow_freq_get_hz()
+{
+    switch(rtc_clk_slow_freq_get()) {
+        case RTC_SLOW_FREQ_RTC: return RTC_SLOW_CLK_FREQ_150K;
+        case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_32K;
+        case RTC_SLOW_FREQ_8MD256: return RTC_SLOW_CLK_FREQ_8MD256;
+    }
+    return 0;
+}
 
 void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq)
 {
@@ -280,7 +316,9 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
     I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_LREF, i2c_bbpll_lref);
     I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
     I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
-    ets_delay_us(DELAY_PLL_ENABLE);
+    uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
+            DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K;
+    ets_delay_us(delay_pll_en);
 }
 
 void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
@@ -291,7 +329,9 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
     REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
     REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
     ets_update_cpu_frequency(xtal_freq);
-    ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_XTAL);
+    uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
+            DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K;
+    ets_delay_us(delay_xtal_switch);
     REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
     SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
             RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD |
@@ -518,7 +558,7 @@ void rtc_clk_init(rtc_clk_config_t cfg)
 
     /* Slow & fast clocks setup */
     if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
-        rtc_clk_32k_enable(false);
+        rtc_clk_32k_enable(true);
     }
     if (cfg.fast_freq == RTC_FAST_FREQ_8M) {
         bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256;
index 9e974cc940fcc22928c29f363be5a4ea57b42189..07a9337a301b83ebc5cb847bc1701b9e965250d7 100644 (file)
  * once, and TIMG_RTC_CALI_RDY bit is set when counting is done. One-off mode is
  * enabled using TIMG_RTC_CALI_START bit.
  */
-uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
+
+/**
+ * @brief Clock calibration function used by rtc_clk_cal and rtc_clk_cal_ratio
+ * @param cal_clk which clock to calibrate
+ * @param slowclk_cycles number of slow clock cycles to count
+ * @return number of XTAL clock cycles within the given number of slow clock cycles
+ */
+static uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
 {
     /* Enable requested clock (150k clock is always on) */
     if (cal_clk == RTC_CAL_32K_XTAL) {
@@ -56,7 +63,7 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
     } else {
         expected_freq = 150000; /* 150k internal oscillator */
     }
-    uint32_t us_time_estimate = slowclk_cycles * MHZ / expected_freq;
+    uint32_t us_time_estimate = (uint32_t) (((uint64_t) slowclk_cycles) * MHZ / expected_freq);
     /* Start calibration */
     CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START);
     SET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START);
@@ -83,8 +90,13 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
         return 0;
     }
 
-    uint64_t xtal_cycles = REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE);
-    uint64_t ratio_64 = (xtal_cycles << RTC_CLK_CAL_FRACT) / slowclk_cycles;
+    return REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE);
+}
+
+uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
+{
+    uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles);
+    uint64_t ratio_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT)) / slowclk_cycles;
     uint32_t ratio = (uint32_t)(ratio_64 & UINT32_MAX);
     return ratio;
 }
@@ -92,8 +104,10 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
 uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
 {
     rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
-    uint32_t ratio = rtc_clk_cal_ratio(cal_clk, slowclk_cycles);
-    uint32_t period = ratio / xtal_freq;
+    uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles);
+    uint64_t divider = ((uint64_t)xtal_freq) * slowclk_cycles;
+    uint64_t period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider;
+    uint32_t period = (uint32_t)(period_64 & UINT32_MAX);
     return period;
 }
 
index b653c48a81baa04e78efecb242166d7bf3f87159..97932d89c3c55ad77acfd5197283bfb33bf3f934 100644 (file)
@@ -27,6 +27,7 @@
 #include "esp32/ulp.h"
 
 #include "soc/soc.h"
+#include "soc/rtc.h"
 #include "soc/rtc_cntl_reg.h"
 #include "soc/sens_reg.h"
 #include "driver/rtc_io.h"
@@ -321,15 +322,11 @@ TEST_CASE("ulp timer setting", "[ulp]")
         uint32_t counter = RTC_SLOW_MEM[offset] & 0xffff;
         CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
         // compare the actual and expected numbers of iterations of ULP program
-        float expected_period = (cycles_to_test[i] + 16) / (float) RTC_CNTL_SLOWCLK_FREQ + 5 / 8e6f;
+        float expected_period = (cycles_to_test[i] + 16) / (float) rtc_clk_slow_freq_get_hz() + 5 / 8e6f;
         float error = 1.0f - counter * expected_period;
         printf("%u\t%u\t%.01f\t%.04f\n", cycles_to_test[i], counter, 1.0f / expected_period, error);
         // Should be within 15%
         TEST_ASSERT_INT_WITHIN(15, 0, (int) error * 100);
-        // Note: currently RTC_CNTL_SLOWCLK_FREQ is ballpark value — we need to determine it
-        // Precisely by running calibration similar to the one done in deep sleep.
-        // This may cause the test to fail on some chips which have the slow clock frequency
-        // way off.
     }
 }
 
index 959fdfbad21b98093778b1d10cfc677fd94d941e..0d0a2fb3ec62e049c0c69113653349901ecb069f 100644 (file)
@@ -22,6 +22,7 @@
 #include "driver/rtc_io.h"
 #include "soc/rtc_cntl_reg.h"
 #include "soc/sens_reg.h"
+#include "soc/rtc.h"
 
 static RTC_DATA_ATTR struct timeval sleep_enter_time;
 
@@ -286,7 +287,7 @@ static void start_ulp_temperature_monitoring()
     assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
 
     // Set ULP wakeup period
-    const uint32_t sleep_cycles = RTC_CNTL_SLOWCLK_FREQ / measurements_per_sec;
+    const uint32_t sleep_cycles = rtc_clk_slow_freq_get_hz() / measurements_per_sec;
     REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles);
 
     // Start ULP