From 44b1bc0ab952f938c0f015ca51ad7f192d65af64 Mon Sep 17 00:00:00 2001 From: Alex Lisitsyn Date: Mon, 26 Feb 2018 22:32:58 +0500 Subject: [PATCH] esp32: Add .noinit and .rtc_noinit sections to the linker script Added .rtc_noinit and .noinit section definitions into linker file /esp32/ld/esp32.common.ld. The macro __NOINIT_ATTR, RTC_NOINIT_ATTR declared in esp32/esp_attr.h file. Added unit test file to test added behavior for noinit variables and its attributes. Added documentation changes for new added attributes. Make some corrections after code review: The linker file has been corrected to place noinit section before bss_start to make it safer. Documentation file has been modified to clarify reset behavior of allocated data . Corrected typos in test_noinit.c and removed assertion of noinit variable to avoid possible issues with ROM boot loader memory allocation. The linker file has been corrected to place noinit section before bss_start to make it safer. Documentation file has been modified to clarify reset behavior of allocated data . Corrected typos in test_noinit.c and removed assertion of noinit variable to avoid possible issues with ROM boot loader memory allocation. Update test_noinit.c file to address RTCWDT_RTC_RESET reset reason instead of POWERON_RESET. Test optimized to pass automated unit testing. esp32: Add .noinit and .rtc_noinit sections to the linker script Update of general-notes.rst documentation to fomat examples as code and attributes as identifiers. Test test_noinit.c corrected to pass automated testing (support ofTEST_CASE_MULTIPLE_STAGES()) https://ezredmine.espressif.cn:8765/issues/15878 --- components/esp32/include/esp_attr.h | 8 ++ components/esp32/ld/esp32.common.ld | 37 +++++++- components/esp32/test/test_noinit.c | 123 +++++++++++++++++++++++++++ docs/en/api-guides/general-notes.rst | 12 +++ 4 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 components/esp32/test/test_noinit.c diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index a9c3f9a7a3..5bf9a22926 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -47,4 +47,12 @@ // Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst" #define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata"))) +// Forces data into noinit section to avoid initialization after restart. +#define __NOINIT_ATTR __attribute__((section(".noinit"))) + +// Forces data into RTC slow memory of .noinit section. +// Any variable marked with this attribute will keep its value +// after restart or during a deep sleep / wake cycle. +#define RTC_NOINIT_ATTR __attribute__((section(".rtc_noinit"))) + #endif /* __ESP_ATTR_H__ */ diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index c6a92356c8..26acd7b2d5 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -11,7 +11,7 @@ SECTIONS . = ALIGN(4); *(.rtc.literal .rtc.text) *rtc_wake_stub*.o(.literal .text .literal.* .text.*) - } >rtc_iram_seg + } > rtc_iram_seg /* RTC slow memory holds RTC wake stub data/rodata, including from any source file @@ -35,6 +35,20 @@ SECTIONS _rtc_bss_end = ABSOLUTE(.); } > rtc_slow_seg + /* This section holds data that should not be initialized at power up + and will be retained during deep sleep. The section located in + RTC SLOW Memory area. User data marked with RTC_NOINIT_ATTR will be placed + into this section. See the file "esp_attr.h" for more information. + */ + .rtc_noinit (NOLOAD): + { + . = ALIGN(4); + _rtc_noinit_start = ABSOLUTE(.); + *(.rtc_noinit .rtc_noinit.*) + . = ALIGN(4) ; + _rtc_noinit_end = ABSOLUTE(.); + } > rtc_slow_seg + /* Send .iram0 code to iram */ .iram0.vectors : { @@ -98,7 +112,7 @@ SECTIONS INCLUDE esp32.spiram.rom-functions-iram.ld _iram_text_end = ABSOLUTE(.); } > iram0_0_seg - + .dram0.data : { _data_start = ABSOLUTE(.); @@ -124,7 +138,21 @@ SECTIONS INCLUDE esp32.spiram.rom-functions-dram.ld _data_end = ABSOLUTE(.); . = ALIGN(4); - } >dram0_0_seg + } > dram0_0_seg + + /*This section holds data that should not be initialized at power up. + The section located in Internal SRAM memory region. The macro _NOINIT + can be used as attribute to place data into this section. + See the esp_attr.h file for more information. + */ + .noinit (NOLOAD): + { + . = ALIGN(4); + _noinit_start = ABSOLUTE(.); + *(.noinit .noinit.*) + . = ALIGN(4) ; + _noinit_end = ABSOLUTE(.); + } > dram0_0_seg /* Shared RAM */ .dram0.bss (NOLOAD) : @@ -147,8 +175,9 @@ SECTIONS *(COMMON) . = ALIGN (8); _bss_end = ABSOLUTE(.); + /* The heap starts right after end of this section */ _heap_start = ABSOLUTE(.); - } >dram0_0_seg + } > dram0_0_seg .flash.rodata : { diff --git a/components/esp32/test/test_noinit.c b/components/esp32/test/test_noinit.c new file mode 100644 index 0000000000..bb1b429392 --- /dev/null +++ b/components/esp32/test/test_noinit.c @@ -0,0 +1,123 @@ +#include "unity.h" +#include "esp_system.h" +#include "rom/rtc.h" +#include "esp_log.h" + +// This is a test sequence to test behavior of .rtc_noinit and .noinit sections. +// The values placed into .rtc_noinit section go to RTC SLOW Memory segment and +// keep their value after reset and deep sleep. Use new added attribute macro +// RTC_NOINIT_ATTR for this behavior. The second macro - __NOINIT_ATTR places value +// into .noinit section which goes to SRAM and will not be initialized after reset. + +#define RTC_NOINIT_PATTERN 0xAAAAAAAA +#define _NOINIT_PATTERN 0x55555555 + +static __NOINIT_ATTR uint32_t noinit_data; +static RTC_NOINIT_ATTR uint32_t rtc_noinit_data; + +extern int _rtc_noinit_start; +extern int _rtc_noinit_end; +extern int _noinit_start; +extern int _noinit_end; + +// Pointers to the values +uint32_t *noinit_val_addr = (uint32_t*)&noinit_data; +uint32_t *rtc_noinit_val_addr = (uint32_t*)&rtc_noinit_data; + +static const char* tag = "noinit_UnitTestMain"; + +static esp_err_t check_data_seg(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 void setup_attributes(void) +{ + rtc_noinit_data = RTC_NOINIT_PATTERN; + noinit_data = _NOINIT_PATTERN; +} + +static void init_attributes(void) +{ + setup_attributes(); + printf("noinit_data = 0x%X \n", (uint32_t)*noinit_val_addr); + printf("rtc_noinit_data = 0x%X \n", (uint32_t)*rtc_noinit_val_addr); + TEST_ASSERT(*noinit_val_addr == noinit_data); + TEST_ASSERT(*rtc_noinit_val_addr == rtc_noinit_data); +} + +static void reset_reason_power_on(void) +{ + printf("This test case checks behavior of noinit variables POWERON_RESET sequence. \n"); + RESET_REASON reason = rtc_get_reset_reason(0); + ESP_LOGI(tag, "POWERON_RESET reset values = (0x%X), (0x%X), reset reason=(%d)\n", \ + (uint32_t)*noinit_val_addr, (uint32_t)*rtc_noinit_val_addr, (uint16_t)reason); + TEST_ASSERT((reason == POWERON_RESET) || (reason == RTCWDT_RTC_RESET)); + + init_attributes(); + TEST_ASSERT(check_data_seg(noinit_val_addr, \ + (uint32_t*)&_noinit_start, \ + (uint32_t*)&_noinit_end) == ESP_OK); + TEST_ASSERT(check_data_seg(rtc_noinit_val_addr, \ + (uint32_t*)&_rtc_noinit_start, \ + (uint32_t*)&_rtc_noinit_end) == ESP_OK); + TEST_ASSERT(_NOINIT_PATTERN == *noinit_val_addr); + TEST_ASSERT(RTC_NOINIT_PATTERN == *rtc_noinit_val_addr); + + printf("Next test case will check SOFTWARE_RESET behavior. \n"); + esp_restart(); +} + +static void reset_reason_sw_reset(void) +{ + printf("This test case checks behavior of noinit variables after software reset sequence. \n"); + RESET_REASON reason = rtc_get_reset_reason(0); + ESP_LOGI(tag, "SW_CPU_RESET reset values = (0x%X), (0x%X), reset reason=(%d)\n", \ + (uint32_t)*noinit_val_addr, (uint32_t)*rtc_noinit_val_addr, (uint16_t)reason); + TEST_ASSERT(reason == SW_CPU_RESET); + TEST_ASSERT(check_data_seg(noinit_val_addr, \ + (uint32_t*)&_noinit_start, \ + (uint32_t*)&_noinit_end) == ESP_OK); + TEST_ASSERT(check_data_seg(rtc_noinit_val_addr, \ + (uint32_t*)&_rtc_noinit_start, \ + (uint32_t*)&_rtc_noinit_end) == ESP_OK); + // The ROM bootloader behavior may apply to this assert. + // TEST_ASSERT(0x55555555 == *noinit_val_addr); + TEST_ASSERT(RTC_NOINIT_PATTERN == *rtc_noinit_val_addr); + printf("Go to deep sleep to check DEEP_SLEEP_RESET behavior. \n"); + esp_sleep_enable_timer_wakeup(2000000); + esp_deep_sleep_start(); +} + +static void reset_reason_deep_sleep(void) +{ + printf("This test case checks behavior of noinit variables after deep sleep reset. \n"); + RESET_REASON reason = rtc_get_reset_reason(0); + ESP_LOGI(tag, "DEEP_SLEEP_RESET reset values = (0x%X), (0x%X), reset reason=(%d)\n", \ + (uint32_t)*noinit_val_addr, (uint32_t)*rtc_noinit_val_addr, (uint16_t)reason); + TEST_ASSERT(reason == DEEPSLEEP_RESET); + TEST_ASSERT(check_data_seg(noinit_val_addr, \ + (uint32_t*)&_noinit_start, \ + (uint32_t*)&_noinit_end) == ESP_OK); + TEST_ASSERT(check_data_seg(rtc_noinit_val_addr, \ + (uint32_t*)&_rtc_noinit_start, \ + (uint32_t*)&_rtc_noinit_end) == ESP_OK); + TEST_ASSERT(RTC_NOINIT_PATTERN == *rtc_noinit_val_addr); + printf("The noinit test cases are done.. \n"); +} + +// 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" +// The multiple stages test case to check values after certain reset reason +TEST_CASE_MULTIPLE_STAGES("NOINIT attributes behavior", + "[restart][reset=SW_CPU_RESET, DEEPSLEEP_RESET]", + reset_reason_power_on, reset_reason_sw_reset, reset_reason_deep_sleep); +#pragma GCC diagnostic pop // require GCC diff --git a/docs/en/api-guides/general-notes.rst b/docs/en/api-guides/general-notes.rst index f3f934968b..71ae1eff4a 100644 --- a/docs/en/api-guides/general-notes.rst +++ b/docs/en/api-guides/general-notes.rst @@ -111,6 +111,12 @@ Constant data may also be placed into DRAM, for example if it is used in an ISR Needless to say, it is not advised to use ``printf`` and other output functions in ISRs. For debugging purposes, use ``ESP_EARLY_LOGx`` macros when logging from ISRs. Make sure that both ``TAG`` and format string are placed into ``DRAM`` in that case. +The macro ``__NOINIT_ATTR`` can be used as attribute to place data into ``.noinit`` section. The values placed into this section will not be initialized at startup and keep its value after software restart. + +Example:: + + __NOINIT_ATTR uint32_t noinit_data; + DROM (data stored in Flash) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,6 +127,12 @@ RTC slow memory Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check detailed description in :doc:`deep sleep ` documentation. +The attribute macro named ``RTC_NOINIT_ATTR`` can be used to place data into this type of memory. The values placed into this section keep their value after waking from deep sleep. + +Example:: + + RTC_NOINIT_ATTR uint32_t rtc_noinit_data; + DMA Capable Requirement -----------------------