summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatryk Duda <pdk@semihalf.com>2022-06-03 09:27:39 +0200
committerDenny Huang <dennyh@google.com>2022-06-07 09:50:01 +0000
commit38b9c4b824f34b3aa81f2399ce91b88077197d52 (patch)
treeafb1a29aa79ad917cee3c99d50fcba5acfb81bd3
parent0c91fc8cfa3c12ffef9260a7c5eb2fce742749ef (diff)
downloadchrome-ec-38b9c4b824f34b3aa81f2399ce91b88077197d52.tar.gz
clock-stm32f4: Fix minimal time the RTC alarm can be set for
Until now, SET_RTC_MATCH_DELAY was set to 120 us which was way too small to setup RTC alarm properly. As a result, watchdog could reset FPMCU while it was in deep sleep (also known as STOP mode in STM32 documentation) with no additional information (because interrupts were disabled in __idle() and the core was sleeping). The problem ocurred when number of microseconds provided to set_rtc_alarm() was between 120 - 125 (typically a value slightly smaller than HOOK_TICK_INTERVAL is provided), so the probability was very low. New value was found by running this code on STM32F4 (placed in chip/stm32/clock-f.c) +volatile uint32_t int_received; + static void __rtc_alarm_irq(void) { + int_received++; + rtc_alarm_irq(); } DECLARE_IRQ(STM32_IRQ_RTC_ALARM, __rtc_alarm_irq, 1); +static int measure_rtc_setup(int argc, char** argv) +{ + struct rtc_time_reg rtc0; + + for (int us = 180; us <= 200; us++) { + int_received = 0; + for (int i = 0; i < 100000; i++) { + set_rtc_alarm(0, us, &rtc0, 0); + usleep(1000); + } + ccprints("%d us - %d received", us, int_received); + } + + return EC_SUCCESS; +} + +DECLARE_CONSOLE_COMMAND(measure_rtc, measure_rtc_setup, "", ""); The code adds 'measure_rtc' console command which is setting RTC alarm and waits 1 ms for interrupt to be triggered. In this code we are testing range from 180us to 200us, 100k times. Results from 120us to 200us, 1k times: [6.667189 120 us - 0 received] [7.680241 121 us - 0 received] [8.693295 122 us - 0 received] [9.706348 123 us - 0 received] [10.719404 124 us - 0 received] [11.732460 125 us - 466 received] [12.745519 126 us - 953 received] [13.758578 127 us - 952 received] [14.771637 128 us - 952 received] [15.784697 129 us - 953 received] [16.797757 130 us - 955 received] ... [67.450714 180 us - 950 received] [68.463772 181 us - 954 received] [69.476831 182 us - 961 received] [70.489890 183 us - 941 received] [71.502949 184 us - 950 received] [72.516008 185 us - 941 received] [73.529068 186 us - 957 received] [74.542128 187 us - 956 received] [75.555187 188 us - 1000 received] [76.568248 189 us - 1000 received] [77.581310 190 us - 1000 received] [78.594371 191 us - 1000 received] [79.607431 192 us - 1000 received] [80.620493 193 us - 1000 received] [81.633554 194 us - 1000 received] [82.646615 195 us - 1000 received] [83.659676 196 us - 1000 received] [84.672737 197 us - 1000 received] [85.685798 198 us - 1000 received] [86.698859 199 us - 1000 received] [87.711920 200 us - 1000 received] We see that with setting RTC alarm to 120us we are guaranteed to not receive an interrupt. Starting from 188us we receive all interrupts. Results from 180us to 200us, 100k times: [107.697820 180 us - 95144 received] [208.997886 181 us - 95250 received] [310.297953 182 us - 95259 received] [411.598017 183 us - 95167 received] [512.898079 184 us - 95245 received] [614.198148 185 us - 95149 received] [715.498217 186 us - 95166 received] [816.798283 187 us - 95235 received] [918.098345 188 us - 100000 received] [1019.398414 189 us - 100000 received] [1120.698484 190 us - 100000 received] [1221.998555 191 us - 100000 received] [1323.298622 192 us - 100000 received] [1424.598695 193 us - 100000 received] [1525.898759 194 us - 100000 received] [1627.198827 195 us - 100000 received] [1728.498897 196 us - 100000 received] [1829.798961 197 us - 100000 received] [1931.099025 198 us - 100000 received] [2032.399095 199 us - 100000 received] [2133.699164 200 us - 100000 received] Again, we see that starting from 188us all interrupts are received. The new value was set to 200us, so we have 12us of safe margin. BUG=b:200828093 BRANCH=none TEST=Set HOOK_TICK_INTERVAL in chip/stm32/config_chip.h to value that is a sum of STOP_MODE_LATENCY, PLL_LOCK_LATENCY and SET_RTC_MATCH_DELAY and value between 50us to 100us (to give enough time to do context switches). For SET_RTC_MATCH_DELAY equal to 120us, good value is 400. For 200us, the value should be 450. Compile bloonchipper and flash it on dragonclaw development board. Make sure that there are no reboots caused by watchdog. TEST=./test/run_device_tests.py --board bloonchipper \ --flasher servo_micro --tests stm32f_rtc Signed-off-by: Patryk Duda <pdk@semihalf.com> Change-Id: Ica2036c142ac7fd792c6365b97cb39fab656d227 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3688190 Reviewed-by: Tom Hughes <tomhughes@chromium.org> Reviewed-by: Craig Hesling <hesling@chromium.org> Reviewed-by: Bobby Casey <bobbycasey@google.com>
-rw-r--r--chip/stm32/clock-stm32f4.c6
-rw-r--r--chip/stm32/config-stm32f4.h11
-rw-r--r--test/stm32f_rtc.c24
3 files changed, 35 insertions, 6 deletions
diff --git a/chip/stm32/clock-stm32f4.c b/chip/stm32/clock-stm32f4.c
index 401c06be1c..b30edc1fa2 100644
--- a/chip/stm32/clock-stm32f4.c
+++ b/chip/stm32/clock-stm32f4.c
@@ -438,12 +438,6 @@ static int dsleep_recovery_margin_us = 1000000;
#define STOP_MODE_LATENCY 50 /* us */
/* PLL_LOCK_LATENCY: delay to switch from HSI to PLL */
#define PLL_LOCK_LATENCY 150 /* us */
-/*
- * SET_RTC_MATCH_DELAY: max time to set RTC match alarm. If we set the alarm
- * in the past, it will never wake up and cause a watchdog.
- */
-#define SET_RTC_MATCH_DELAY 120 /* us */
-
void low_power_init(void)
{
diff --git a/chip/stm32/config-stm32f4.h b/chip/stm32/config-stm32f4.h
index 46303444fc..ee1d594116 100644
--- a/chip/stm32/config-stm32f4.h
+++ b/chip/stm32/config-stm32f4.h
@@ -73,3 +73,14 @@
/* DFU Address */
#define STM32_DFU_BASE 0x1fff0000
+
+/*
+ * SET_RTC_MATCH_DELAY: max time to set RTC match alarm. If we set the alarm
+ * in the past, it will never wake up and cause a watchdog.
+ *
+ * This value is minimal time for which we can set RTC match alarm. It was
+ * obtained by setting the alarm and checking if the alarm interrupt was
+ * triggered. Unit test test_rtc_match_delay is responsible for verifying if
+ * setting the RTC match alarm with this value will generate the interrupt.
+ */
+#define SET_RTC_MATCH_DELAY 200 /* us */
diff --git a/test/stm32f_rtc.c b/test/stm32f_rtc.c
index 36c67f004e..b9b48ec043 100644
--- a/test/stm32f_rtc.c
+++ b/test/stm32f_rtc.c
@@ -53,11 +53,35 @@ test_static int test_rtc_alarm(void)
return EC_SUCCESS;
}
+static const int rtc_match_delay_iterations = 5000;
+
+test_static int test_rtc_match_delay(void)
+{
+ struct rtc_time_reg rtc;
+ int i;
+
+ atomic_clear(&rtc_fired);
+ for (i = 0; i < rtc_match_delay_iterations; i++) {
+ set_rtc_alarm(0, SET_RTC_MATCH_DELAY, &rtc, 0);
+ usleep(2 * SET_RTC_MATCH_DELAY);
+ }
+
+ ccprintf("Expected number of RTC alarm interrupts %d\n",
+ rtc_match_delay_iterations);
+ ccprintf("Actual number of RTC alarm interrupts %d\n", rtc_fired);
+
+ /* Make sure each set_rtc_alarm() generated the interrupt. */
+ TEST_EQ(rtc_match_delay_iterations, atomic_clear(&rtc_fired), "%d");
+
+ return EC_SUCCESS;
+}
+
void run_test(int argc, char **argv)
{
test_reset();
RUN_TEST(test_rtc_alarm);
+ RUN_TEST(test_rtc_match_delay);
test_print_result();
}