From cdef1ea38a29773c7fb1c28b33074a789638ef87 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 11 Mar 2021 21:17:48 +1100 Subject: [PATCH] examples: Add example for fastest startup time Example includes README and sdkconfig.defaults with notes about trade-offs made for minimum boot time. --- examples/system/startup_time/CMakeLists.txt | 6 ++ examples/system/startup_time/Makefile | 8 +++ examples/system/startup_time/README.md | 63 +++++++++++++++++++ examples/system/startup_time/example_test.py | 30 +++++++++ .../system/startup_time/main/CMakeLists.txt | 2 + .../system/startup_time/main/component.mk | 4 ++ .../startup_time/main/hello_world_main.c | 23 +++++++ .../system/startup_time/sdkconfig.defaults | 19 ++++++ .../startup_time/sdkconfig.defaults.esp32 | 5 ++ .../startup_time/sdkconfig.defaults.esp32c3 | 5 ++ .../startup_time/sdkconfig.defaults.esp32s2 | 5 ++ .../startup_time/sdkconfig.defaults.esp32s3 | 5 ++ tools/ci/mypy_ignore_list.txt | 1 + 13 files changed, 176 insertions(+) create mode 100644 examples/system/startup_time/CMakeLists.txt create mode 100644 examples/system/startup_time/Makefile create mode 100644 examples/system/startup_time/README.md create mode 100644 examples/system/startup_time/example_test.py create mode 100644 examples/system/startup_time/main/CMakeLists.txt create mode 100644 examples/system/startup_time/main/component.mk create mode 100644 examples/system/startup_time/main/hello_world_main.c create mode 100644 examples/system/startup_time/sdkconfig.defaults create mode 100644 examples/system/startup_time/sdkconfig.defaults.esp32 create mode 100644 examples/system/startup_time/sdkconfig.defaults.esp32c3 create mode 100644 examples/system/startup_time/sdkconfig.defaults.esp32s2 create mode 100644 examples/system/startup_time/sdkconfig.defaults.esp32s3 diff --git a/examples/system/startup_time/CMakeLists.txt b/examples/system/startup_time/CMakeLists.txt new file mode 100644 index 0000000000..4107f94ac5 --- /dev/null +++ b/examples/system/startup_time/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(startup_time) diff --git a/examples/system/startup_time/Makefile b/examples/system/startup_time/Makefile new file mode 100644 index 0000000000..1f1264300b --- /dev/null +++ b/examples/system/startup_time/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := startup_time + +include $(IDF_PATH)/make/project.mk diff --git a/examples/system/startup_time/README.md b/examples/system/startup_time/README.md new file mode 100644 index 0000000000..3a64a41912 --- /dev/null +++ b/examples/system/startup_time/README.md @@ -0,0 +1,63 @@ +# Startup Time Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example demonstrates the configuration settings to obtain the minimum possible startup time for an ESP-IDF application (i.e. time from initial reset until the `app_main()` function is running). + +Note that some of the configuration settings in `sdkconfig.defaults` have trade-offs that may not be suitable for your project, see the "Applying To Your Own Project" section at the bottom for more details. + +## How to use example + +### Hardware Required + +This example should be able to run on any commonly available ESP32 development board. + +### Configure the project + +This step is optional, the default settings in `sdkconfig.defaults` are already set to minimize startup time. + +``` +idf.py menuconfig +``` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +The example should have log output similar to the following: + +``` +W (34) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +I (37) example: App started! +``` + +All early log output is disabled in order to save time (it's also possible to disable the ROM log output immediately after reset, see below.) + +Note that boot time reported in the log timestamp may vary depending on the chip target, chip revision, and eFuse configuration. + +## Applying To Your Own Project + +The file `sdkconfig.defaults` contains a range of setting which can be applied to any project in order for it to boot more rapidly. + +However, note that some of these settings may have tradeoffs - for example not all hardware may support all flash modes and speeds, some applications would prefer the reliability of checking the application on every boot rather than waiting for a crash and then checking the application, and some applications will use features such as Secure Boot which require additional overhead on boot. + +The `sdkconfig.defaults` file in this directory contains comments above each of the settings to optimize boot speed. To add the settings +to your own project, either search for the setting name in `menuconfig` or exit `menuconfig` and then copy-paste the setting lines at the end of your project's `sdkconfig` file. + +## Additional Startup Speed + +Removing the default boot log output printed by the ROM can shave several milliseconds off the SoC boot time. The default configuration doesn't make this change, as it is done via eFuse the change is permanent. + +If you wish to make this change run `idf.py menuconfig`, navigate to "Boot ROM Behavior" and set the "Permanently change Boot ROM output" option. diff --git a/examples/system/startup_time/example_test.py b/examples/system/startup_time/example_test.py new file mode 100644 index 0000000000..fabbc8acb1 --- /dev/null +++ b/examples/system/startup_time/example_test.py @@ -0,0 +1,30 @@ +from __future__ import print_function + +import re + +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2', 'esp32c3']) +def test_startup_time_example(env, extra_data): + app_name = 'startup_time' + dut = env.get_dut(app_name, 'examples/system/startup_time') + dut.start_app() + + res = dut.expect(re.compile(r'\((\d+)\) [^:]+: App started!')) + time = int(res[0]) + + # Allow ci-dashboard to track startup times + print('------ startup time info ------\n' + '[app_name] {}\n' + '[startup_time] {}\n' + '[config] {}\n' + '[target] {}\n' + '------ startup time end ------'.format(app_name, + time, + dut.app.config_name, + dut.TARGET)) + + +if __name__ == '__main__': + test_startup_time_example() diff --git a/examples/system/startup_time/main/CMakeLists.txt b/examples/system/startup_time/main/CMakeLists.txt new file mode 100644 index 0000000000..07686dc8e1 --- /dev/null +++ b/examples/system/startup_time/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "hello_world_main.c" + INCLUDE_DIRS "") diff --git a/examples/system/startup_time/main/component.mk b/examples/system/startup_time/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/system/startup_time/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/system/startup_time/main/hello_world_main.c b/examples/system/startup_time/main/hello_world_main.c new file mode 100644 index 0000000000..76c6ab6f7f --- /dev/null +++ b/examples/system/startup_time/main/hello_world_main.c @@ -0,0 +1,23 @@ +/* Startup time example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "esp_log.h" + +static const char *TAG = "example"; + +/* The purpose of this app is to demonstrate fast startup time only, so feel free + to replace this app_main() with your own code or copy the sdkconfig.defaults contents + into a different project's sdkconfig file. +*/ +void app_main(void) +{ + // Calling this function restores all Info-level logging at runtime (as "Log Maximum Verbosity" set to "Info") + esp_log_level_set("*", ESP_LOG_INFO); + ESP_LOGI(TAG, "App started!"); +} diff --git a/examples/system/startup_time/sdkconfig.defaults b/examples/system/startup_time/sdkconfig.defaults new file mode 100644 index 0000000000..f57b21b2e4 --- /dev/null +++ b/examples/system/startup_time/sdkconfig.defaults @@ -0,0 +1,19 @@ +# Set flash configuration as fast as possible (Quad I/O 80MHz) +# +# (Not all hardware may support this configuration.) +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y + +# These two settings mean that no logs are printed +# during startup, but it's possible to use esp_log_level_set("*", ESP_LOG_INFO) +# at runtime to get Info-level logging back +CONFIG_LOG_DEFAULT_LEVEL_WARN=y +CONFIG_LOG_MAXIMUM_LEVEL_INFO=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y + +# at risk of not detecting flash corruption, skip bootloader +# verification of the app unless a soft reset or crash happened +# +# note: the larger the application, the bigger the time saved by +# from this option +CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON=y diff --git a/examples/system/startup_time/sdkconfig.defaults.esp32 b/examples/system/startup_time/sdkconfig.defaults.esp32 new file mode 100644 index 0000000000..719d458d99 --- /dev/null +++ b/examples/system/startup_time/sdkconfig.defaults.esp32 @@ -0,0 +1,5 @@ +# Not calibrating RTC_SLOW_CLK saves a small amount of time during boot. +# +# Setting option to zero is only recommended if not using sleep modes, or +# if you don't need accurate sleep times. +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=0 diff --git a/examples/system/startup_time/sdkconfig.defaults.esp32c3 b/examples/system/startup_time/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..fe85ce3ed8 --- /dev/null +++ b/examples/system/startup_time/sdkconfig.defaults.esp32c3 @@ -0,0 +1,5 @@ +# Not calibrating RTC_SLOW_CLK saves a small amount of time during boot. +# +# Setting option to zero is only recommended if not using sleep modes, or +# if you don't need accurate sleep times. +CONFIG_ESP32C3_RTC_CLK_CAL_CYCLES=0 diff --git a/examples/system/startup_time/sdkconfig.defaults.esp32s2 b/examples/system/startup_time/sdkconfig.defaults.esp32s2 new file mode 100644 index 0000000000..a0a53fe8f0 --- /dev/null +++ b/examples/system/startup_time/sdkconfig.defaults.esp32s2 @@ -0,0 +1,5 @@ +# Not calibrating RTC_SLOW_CLK saves a small amount of time during boot. +# +# Setting option to zero is only recommended if not using sleep modes, or +# if you don't need accurate sleep times. +CONFIG_ESP32S2_RTC_CLK_CAL_CYCLES=0 diff --git a/examples/system/startup_time/sdkconfig.defaults.esp32s3 b/examples/system/startup_time/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..468d08f971 --- /dev/null +++ b/examples/system/startup_time/sdkconfig.defaults.esp32s3 @@ -0,0 +1,5 @@ +# Not calibrating RTC_SLOW_CLK saves a small amount of time during boot. +# +# Setting option to zero is only recommended if not using sleep modes, or +# if you don't need accurate sleep times. +CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=0 diff --git a/tools/ci/mypy_ignore_list.txt b/tools/ci/mypy_ignore_list.txt index a76883839b..c29ea9ca44 100644 --- a/tools/ci/mypy_ignore_list.txt +++ b/tools/ci/mypy_ignore_list.txt @@ -145,6 +145,7 @@ examples/system/ota/otatool/otatool_example.py examples/system/ota/simple_ota_example/example_test.py examples/system/perfmon/example_test.py examples/system/select/example_test.py +examples/system/startup_time/example_test.py examples/system/sysview_tracing/example_test.py examples/system/sysview_tracing_heap_log/example_test.py examples/system/task_watchdog/example_test.py