diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 68b22aca6..dca24eb24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1090,6 +1090,20 @@ UT_004_11: - UT_T1_1 - psram +UT_004_12: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + - psram + +UT_004_13: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + - psram + UT_005_01: <<: *unit_test_template tags: @@ -1104,6 +1118,13 @@ UT_005_02: - UT_T1_SPIMODE - psram +UT_005_03: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_SPIMODE + - psram + UT_006_01: <<: *unit_test_template tags: diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index aa7415f57..adbc7d376 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -867,6 +867,16 @@ config ESP_ERR_TO_NAME_LOOKUP order to save memory but this comes at the price of sacrificing distinguishable (meaningful) output string representations. +config ESP32_RTCDATA_IN_FAST_MEM + bool "Place RTC_DATA_ATTR and RTC_RODATA_ATTR variables into RTC fast memory segment" + default n + depends on FREERTOS_UNICORE + help + This option allows to place .rtc_data and .rtc_rodata sections into + RTC fast memory segment to free the slow memory region for ULP programs. + This option depends on the CONFIG_FREERTOS_UNICORE option because RTC fast memory + can be accessed only by PRO_CPU core. + endmenu # ESP32-Specific menu Wi-Fi diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 5bf9a2292..b158224d3 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -44,9 +44,15 @@ // during a deep sleep / wake cycle. #define RTC_DATA_ATTR __attribute__((section(".rtc.data"))) -// Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst" +// Forces read-only data into RTC memory. See "docs/deep-sleep-stub.rst" #define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata"))) +// Allows to place data into RTC_SLOW memory. +#define RTC_SLOW_ATTR __attribute__((section(".rtc.force_slow"))) + +// Allows to place data into RTC_FAST memory. +#define RTC_FAST_ATTR __attribute__((section(".rtc.force_fast"))) + // Forces data into noinit section to avoid initialization after restart. #define __NOINIT_ATTR __attribute__((section(".noinit"))) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index cf1a3a7a9..75ebd82f0 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -11,11 +11,40 @@ SECTIONS . = ALIGN(4); *(.rtc.literal .rtc.text) *rtc_wake_stub*.*(.literal .text .literal.* .text.*) + _rtc_text_end = ABSOLUTE(.); } > rtc_iram_seg + + /* + This section is required to skip rtc.text area because rtc_iram_seg and + rtc_data_seg are reflect the same address space on different buses. + */ + .rtc.dummy : + { + _rtc_dummy_start = ABSOLUTE(.); + _rtc_fast_start = ABSOLUTE(.); + . = SIZEOF(.rtc.text); + _rtc_dummy_end = ABSOLUTE(.); + } > rtc_data_seg - /* RTC slow memory holds RTC wake stub + /* This section located in RTC FAST Memory area. + It holds data marked with RTC_FAST_ATTR attribute. + See the file "esp_attr.h" for more information. + */ + .rtc.force_fast : + { + . = ALIGN(4); + _rtc_force_fast_start = ABSOLUTE(.); + *(.rtc.force_fast .rtc.force_fast.*) + . = ALIGN(4) ; + _rtc_force_fast_end = ABSOLUTE(.); + } > rtc_data_seg + + /* RTC data section holds RTC wake stub data/rodata, including from any source file - named rtc_wake_stub*.c + named rtc_wake_stub*.c and the data marked with + RTC_DATA_ATTR, RTC_RODATA_ATTR attributes. + The memory location of the data dependent on + CONFIG_ESP32_RTCDATA_IN_FAST_MEM option. */ .rtc.data : { @@ -24,7 +53,7 @@ SECTIONS *(.rtc.rodata) *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*) _rtc_data_end = ABSOLUTE(.); - } > rtc_slow_seg + } > rtc_data_location /* RTC bss, from any source file named rtc_wake_stub*.c */ .rtc.bss (NOLOAD) : @@ -34,6 +63,19 @@ SECTIONS *rtc_wake_stub*.*(COMMON) *(.rtc.bss) _rtc_bss_end = ABSOLUTE(.); + } > rtc_data_location + + /* This section located in RTC SLOW Memory area. + It holds data marked with RTC_SLOW_ATTR attribute. + See the file "esp_attr.h" for more information. + */ + .rtc.force_slow : + { + . = ALIGN(4); + _rtc_force_slow_start = ABSOLUTE(.); + *(.rtc.force_slow .rtc.force_slow.*) + . = ALIGN(4) ; + _rtc_force_slow_end = ABSOLUTE(.); } > rtc_slow_seg /* This section holds data that should not be initialized at power up @@ -50,6 +92,21 @@ SECTIONS _rtc_noinit_end = ABSOLUTE(.); } > rtc_slow_seg + /* Get size of rtc slow data based on rtc_data_location alias */ + _rtc_slow_length = (ORIGIN(rtc_slow_seg) == ORIGIN(rtc_data_location)) + ? (_rtc_noinit_end - _rtc_data_start) + : (_rtc_noinit_end - _rtc_force_slow_start); + + _rtc_fast_length = (ORIGIN(rtc_slow_seg) == ORIGIN(rtc_data_location)) + ? (_rtc_force_fast_end - _rtc_fast_start) + : (_rtc_bss_end - _rtc_fast_start); + + ASSERT((_rtc_slow_length <= LENGTH(rtc_slow_seg)), + "RTC_SLOW segment data does not fit.") + + ASSERT((_rtc_fast_length <= LENGTH(rtc_data_seg)), + "RTC_FAST segment data does not fit.") + /* Send .iram0 code to iram */ .iram0.vectors : { @@ -87,7 +144,7 @@ SECTIONS *(.init.literal) *(.init) _init_end = ABSOLUTE(.); -} > iram0_0_seg + } > iram0_0_seg .iram0.text : { @@ -113,6 +170,9 @@ SECTIONS _iram_end = ABSOLUTE(.); } > iram0_0_seg + ASSERT(((_iram_text_end - ORIGIN(iram0_0_seg)) <= LENGTH(iram0_0_seg)), + "IRAM0 segment data does not fit.") + .dram0.data : { _data_start = ABSOLUTE(.); @@ -195,6 +255,9 @@ SECTIONS _heap_start = ABSOLUTE(.); } > dram0_0_seg + ASSERT(((_bss_end - ORIGIN(dram0_0_seg)) <= LENGTH(dram0_0_seg)), + "DRAM segment data does not fit.") + .flash.rodata : { _rodata_start = ABSOLUTE(.); diff --git a/components/esp32/ld/esp32.ld b/components/esp32/ld/esp32.ld index ee41f7479..3c404e1aa 100644 --- a/components/esp32/ld/esp32.ld +++ b/components/esp32/ld/esp32.ld @@ -61,6 +61,9 @@ MEMORY /* RTC fast memory (executable). Persists over deep sleep. */ rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000 + + /* RTC fast memory (same block as above), viewed from data bus */ + rtc_data_seg(RW) : org = 0x3ff80000, len = 0x2000 /* RTC slow memory (data accessible). Persists over deep sleep. @@ -72,3 +75,14 @@ MEMORY /* Heap ends at top of dram0_0_seg */ _heap_end = 0x40000000 - CONFIG_TRACEMEM_RESERVE_DRAM; + +_data_seg_org = ORIGIN(rtc_data_seg); + +/* The lines below define location alias for .rtc.data section based on Kconfig option. + When the option is not defined then use slow memory segment + else the data will be placed in fast memory segment */ +#ifndef CONFIG_ESP32_RTCDATA_IN_FAST_MEM +REGION_ALIAS("rtc_data_location", rtc_slow_seg ); +#else +REGION_ALIAS("rtc_data_location", rtc_data_seg ); +#endif diff --git a/components/esp32/test/test_rtc_fast.c b/components/esp32/test/test_rtc_fast.c new file mode 100644 index 000000000..5d5a191b6 --- /dev/null +++ b/components/esp32/test/test_rtc_fast.c @@ -0,0 +1,260 @@ +// The lines below allow to skip test in configuration other then +// single_core which is required for this test to check placement in RTC_FAST memory +#include "sdkconfig.h" + +#ifdef CONFIG_ESP32_RTCDATA_IN_FAST_MEM + +#include "unity.h" +#include "esp_system.h" +#include "rom/rtc.h" // for rtc defines +#include "rom/uart.h" // for uart_tx_wait_idle() +#include "esp_log.h" // for log write functionality +#include "driver/rtc_io.h" // for gpio configuration +#include "esp_sleep.h" // include sleep related functionality +#include "soc/soc.h" // include access to soc macros +#include "soc/timer_group_reg.h" // for watchdog register defines +#include "soc/rtc_cntl_reg.h" // for rtc cntl register defines +#include "freertos/FreeRTOS.h" // for xPortGetCoreID + +// Test notes: +// This test case sequence checks behavior of placement .rtc_data and .rtc_rodata sections +// into RTC_FAST memory. The Kconfig option CONFIG_ESP32_RTCDATA_IN_FAST_MEM +// is used to configure this behavior. The RTC_DATA_ATTR, RTC_RODATA_ATTR attributes +// can be used to place data into this area. If option is not set the .rtc_data +// and .rtc_rodata are placed in slow memory segment for compatibility. +// The only PRO_CPU can access the RTC_FAST memory, so the option CONFIG_FREERTOS_UNICORE +// should be enabled to place data into RTC_FAST memory segment. + +#define ESP_EXT0_WAKEUP_LEVEL_LOW 0 +#define ESP_EXT0_WAKEUP_LEVEL_HIGH 1 + +#define RTC_DATA_PATTERN 0xAAAAAAAA +#define RTC_RODATA_PATTERN 0x55555555 +#define RTC_FAST_PATTERN 0xDDDDDDDD +#define RTC_SLOW_PATTERN 0x99999999 +#define WAKE_STUB_PATTERN 0x77777777 + +#ifdef CONFIG_ESP32_RTCDATA_IN_FAST_MEM +#define CHECK_RTC_FAST_OPTION_ENABLED 1 +#else +#define CHECK_RTC_FAST_OPTION_ENABLED 0 +#endif + +static RTC_DATA_ATTR uint32_t rtc_data = RTC_DATA_PATTERN; +static RTC_RODATA_ATTR uint32_t rtc_rodata = RTC_RODATA_PATTERN; +static RTC_FAST_ATTR uint32_t rtc_force_fast = RTC_FAST_PATTERN; +static RTC_SLOW_ATTR uint32_t rtc_force_slow = RTC_SLOW_PATTERN; + +extern int _rtc_data_start; +extern int _rtc_data_end; +extern int _rtc_force_fast_start; +extern int _rtc_force_fast_end; +extern int _rtc_force_slow_start; +extern int _rtc_force_slow_end; + +// This points to the values in RTC memory +static uint32_t *rtc_data_val_addr = (uint32_t*)&rtc_data; +static uint32_t *rtc_rodata_val_addr = (uint32_t*)&rtc_rodata; +static uint32_t *rtc_fast_val_addr = (uint32_t*)&rtc_force_fast; +static uint32_t *rtc_slow_val_addr = (uint32_t*)&rtc_force_slow; + +static const char* tag = "rtc_data_fast_UnitTestMain"; + +static void RTC_IRAM_ATTR wake_stub(void); + +static void RTC_IRAM_ATTR wake_stub_dummy(void) +{ + rtc_data = WAKE_STUB_PATTERN; + set_rtc_memory_crc(); // update rtc memory CRC +} + +static esp_err_t check_data_placement(uint32_t *value_address, + uint32_t *seg_start, uint32_t *seg_end) +{ + esp_err_t result = ESP_FAIL; + if (((uint32_t)value_address <= (uint32_t)seg_end) + && ((uint32_t)value_address >= (uint32_t)seg_start)){ + result = ESP_OK; + } + return result; +} + +static esp_err_t check_wake_stub_status(void) +{ + esp_err_t result = ESP_FAIL; + + uint32_t entry_addr = REG_READ(RTC_ENTRY_ADDR_REG); + + if (entry_addr == (uint32_t)&wake_stub_dummy) { + result = ESP_OK; + } else if (entry_addr == (uint32_t)&wake_stub) { + result = ESP_ERR_INVALID_STATE; + } else { + result = ESP_FAIL; + } + + return result; +} + +static void RTC_IRAM_ATTR wake_stub(void) +{ + esp_default_wake_deep_sleep(); + + // Set the pointer of the new wake stub function. + // It will be checked in test to make sure the wake stub entered + REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)&wake_stub_dummy); + + // Set this value to check it in test later + rtc_data = WAKE_STUB_PATTERN; + set_rtc_memory_crc(); // update rtc memory CRC +} + +static void setup_deep_sleep(void) +{ + // Set wake stub function to check its behavior + // This function sets checksum of RTC fast memory appropriately + esp_set_deep_sleep_wake_stub(&wake_stub); + + // Setup ext0 configuration to wake up immediately + ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13)); + ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, ESP_EXT0_WAKEUP_LEVEL_HIGH)); + uart_tx_wait_idle(0); + esp_deep_sleep_start(); +} + +// The lines below are required to suppress GCC warnings about casting of function pointers +// in unity macro expansion. These warnings may be treated as errors during automated test. +#pragma GCC diagnostic push // required for GCC +#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" +static void reset_reason_power_on(void) +{ + printf("This test case verifies behavior of RTC_DATA variables after reset. \n"); + RESET_REASON reason = rtc_get_reset_reason(0); + TEST_ASSERT(reason == POWERON_RESET || reason == RTCWDT_RTC_RESET); + + ESP_LOGI(tag, "Reset reason=(%d)", (uint16_t)reason); + + printf("rtc_data_val = (0x%X), rtc_rodata_val = (0x%X), rtc_fast_val = (0x%X), rtc_slow_val = (0x%X)\r\n", + (uint32_t)*rtc_data_val_addr, (uint32_t)*rtc_rodata_val_addr, + (uint32_t)*rtc_fast_val_addr, (uint32_t)*rtc_slow_val_addr); + + printf("This test case called by CPU%u\r\n", (uint16_t)xPortGetCoreID()); + + TEST_ASSERT(CHECK_RTC_FAST_OPTION_ENABLED == 1); + printf("Check that values are placed in correct sections and then compare its values with patterns.\r\n"); + + TEST_ASSERT(check_data_placement(rtc_fast_val_addr, + (uint32_t*)&_rtc_force_fast_start, + (uint32_t*)&_rtc_force_fast_end) == ESP_OK); + + TEST_ASSERT(check_data_placement(rtc_data_val_addr, + (uint32_t*)&_rtc_data_start, + (uint32_t*)&_rtc_data_end) == ESP_OK); + + TEST_ASSERT(check_data_placement(rtc_rodata_val_addr, + (uint32_t*)&_rtc_data_start, + (uint32_t*)&_rtc_data_end) == ESP_OK); + + printf("Values RTC_DATA_ATTR, RTC_FAST_ATTR are placed in section (0x%x - 0x%x).\r\n", + (uint32_t)&_rtc_data_start, (uint32_t)&_rtc_data_end); + + TEST_ASSERT(check_data_placement(rtc_slow_val_addr, + (uint32_t*)&_rtc_force_slow_start, + (uint32_t*)&_rtc_force_slow_end) == ESP_OK); + + printf("The RTC_SLOW values are placed in slow memory (0x%X - 0x%X).\r\n", + (uint32_t)&_rtc_force_slow_start, (uint32_t)&_rtc_force_slow_end); + + TEST_ASSERT(RTC_RODATA_PATTERN == *rtc_rodata_val_addr); + TEST_ASSERT(RTC_DATA_PATTERN == *rtc_data_val_addr); + TEST_ASSERT(RTC_FAST_PATTERN == *rtc_fast_val_addr); + + TEST_ASSERT(RTC_SLOW_PATTERN == *rtc_slow_val_addr); + + printf("The values correspond to its patterns.\r\n"); + printf("Go to deep sleep to check DEEP_SLEEP_RESET behavior. \r\n"); + setup_deep_sleep(); +} + +static void reset_reason_deep_sleep(void) +{ + printf("This test case checks behavior of RTC_DATA variables after deep sleep reset. \r\n"); + RESET_REASON reason = rtc_get_reset_reason(0); + + ESP_LOGI(tag, "Reset reason=(%d)", (uint16_t)reason); + + printf("rtc_data_val = (0x%X), rtc_rodata_val = (0x%X), rtc_fast_val = (0x%X), rtc_slow_val = (0x%X)\r\n", + (uint32_t)*rtc_data_val_addr, (uint32_t)*rtc_rodata_val_addr, + (uint32_t)*rtc_fast_val_addr, (uint32_t)*rtc_slow_val_addr); + + TEST_ASSERT(reason == DEEPSLEEP_RESET); + + if (CHECK_RTC_FAST_OPTION_ENABLED == 1) { + printf("The CONFIG_ESP32_RTCDATA_IN_FAST_MEM is active means placement of RTC_DATA in fast segment.\r\n"); + } else { + printf("The CONFIG_ESP32_RTCDATA_IN_FAST_MEM is not set. Please set this option\r\n"); + printf("in menuconfig to check .rtc.data placement in RTC fast memory.\r\n"); + } + + TEST_ASSERT(CHECK_RTC_FAST_OPTION_ENABLED == 1); + + // Check if deep sleep wake stub has been entered + // this means CRC was correct + if (check_wake_stub_status() == ESP_OK) { + printf("The wake stub has been executed means CRC was correct.\r\n"); + } else { + printf("The wake stub is not executed.\r\n"); + } + + TEST_ASSERT(check_wake_stub_status() == ESP_OK); + + // If the RTC CRC is incorrect the function return NULL + if (esp_get_deep_sleep_wake_stub() == NULL){ + printf("The wake stub has changed the value of rtc_data as expected.\r\n"); + } else { + printf("The rtc_data value is not changed that is incorrect. \r\n"); + } + + TEST_ASSERT(esp_get_deep_sleep_wake_stub() == NULL); + + TEST_ASSERT(check_data_placement(rtc_data_val_addr, + (uint32_t*)&_rtc_data_start, + (uint32_t*)&_rtc_data_end) == ESP_OK); + + TEST_ASSERT(check_data_placement(rtc_rodata_val_addr, + (uint32_t*)&_rtc_data_start, + (uint32_t*)&_rtc_data_end) == ESP_OK); + + TEST_ASSERT(check_data_placement(rtc_fast_val_addr, + (uint32_t*)&_rtc_force_fast_start, + (uint32_t*)&_rtc_force_fast_end) == ESP_OK); + + printf("Values RTC_DATA_ATTR, RTC_FAST_ATTR are placed in RTC fast segment (0x%x - 0x%x).\r\n", + (uint32_t)&_rtc_data_start, (uint32_t)&_rtc_data_end); + + TEST_ASSERT(check_data_placement(rtc_slow_val_addr, + (uint32_t*)&_rtc_force_slow_start, + (uint32_t*)&_rtc_force_slow_end) == ESP_OK); + + printf("The RTC_SLOW values are placed in slow memory (0x%X - 0x%X).\r\n", + (uint32_t)&_rtc_force_slow_start, (uint32_t)&_rtc_force_slow_end); + + TEST_ASSERT(RTC_DATA_PATTERN == *rtc_data_val_addr); + TEST_ASSERT(RTC_RODATA_PATTERN == *rtc_rodata_val_addr); + TEST_ASSERT(RTC_FAST_PATTERN == *rtc_fast_val_addr); + + TEST_ASSERT(RTC_SLOW_PATTERN == *rtc_slow_val_addr); + + printf("Values correspond to its patterns.\r\n"); + printf("The test cases are done.. \r\n"); +} + +// The multiple stages test case to check values after certain reset reason +TEST_CASE_MULTIPLE_STAGES("RTC_DATA attributes behavior", \ + "[restart][reset=DEEPSLEEP_RESET]", + reset_reason_power_on, reset_reason_deep_sleep); +#pragma GCC diagnostic pop // require GCC + +#endif // CONFIG_ESP32_RTCDATA_IN_FAST_MEM diff --git a/docs/en/api-guides/deep-sleep-stub.rst b/docs/en/api-guides/deep-sleep-stub.rst index 7711b1ce2..6fd547271 100644 --- a/docs/en/api-guides/deep-sleep-stub.rst +++ b/docs/en/api-guides/deep-sleep-stub.rst @@ -55,11 +55,11 @@ The first way is simpler for very short and simple code, or for source files whe Loading Data Into RTC Memory ---------------------------- -Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP. +Data used by stub code must be resident in RTC memory. The data can be placed in RTC Fast memory or in RTC Slow memory which is also used by the ULP. Specifying this data can be done in one of two ways: -The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectivley) which should be loaded into RTC slow memory:: +The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectively) which should be loaded into RTC memory:: RTC_DATA_ATTR int wake_count; @@ -69,6 +69,10 @@ The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify ets_printf(fmt_str, wake_count++); } +The RTC memory area where this data will be placed can be configured via menuconfig option named ``CONFIG_ESP32_RTCDATA_IN_FAST_MEM``. This option allows to keep slow memory area for ULP programs and once it is enabled the data marked with ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` are placed in the RTC fast memory segment otherwise it goes to RTC slow memory (default option). This option depends on the ``CONFIG_FREERTOS_UNICORE`` because RTC fast memory can be accessed only by PRO_CPU. + +The similar attributes ``RTC_FAST_ATTR`` and ``RTC_SLOW_ATTR`` can be used to specify data that will be force placed into RTC_FAST and RTC_SLOW memory respectively. Any access to data marked with ``RTC_FAST_ATTR`` is allowed by PRO_CPU only and it is responsibility of user to make sure about it. + Unfortunately, any string constants used in this way must be declared as arrays and marked with RTC_RODATA_ATTR, as shown in the example above. The second way is to place the data into any source file whose name starts with ``rtc_wake_stub``. diff --git a/tools/unit-test-app/configs/single_core b/tools/unit-test-app/configs/single_core index 21d11cbad..9c85abae6 100644 --- a/tools/unit-test-app/configs/single_core +++ b/tools/unit-test-app/configs/single_core @@ -1,3 +1,4 @@ TEST_EXCLUDE_COMPONENTS=libsodium bt app_update CONFIG_MEMMAP_SMP=n -CONFIG_FREERTOS_UNICORE=y \ No newline at end of file +CONFIG_FREERTOS_UNICORE=y +CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y