]> granicus.if.org Git - esp-idf/commitdiff
esp32: Add .noinit and .rtc_noinit sections to the linker script
authorAlex Lisitsyn <aleks@espressif.com>
Mon, 26 Feb 2018 17:32:58 +0000 (22:32 +0500)
committerAlex Lisitsyn <aleks@espressif.com>
Wed, 16 May 2018 12:33:37 +0000 (17:33 +0500)
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
components/esp32/ld/esp32.common.ld
components/esp32/test/test_noinit.c [new file with mode: 0644]
docs/en/api-guides/general-notes.rst

index a9c3f9a7a33c40cbc015be7bc35ab1a188eb92a0..5bf9a22926c04b9eaf1da4d4715e5b372c0de0dc 100644 (file)
 // 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__ */
index c6a92356c8fd70d8f7575cd2e6f203449fb24355..26acd7b2d59ea8220b19f310259bfe34de2f9b22 100644 (file)
@@ -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 (file)
index 0000000..bb1b429
--- /dev/null
@@ -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
index f3f934968b939a63bf05275a40ef96ea81547a09..71ae1eff4ac3a0d112c67bbad5a38554f8a41d21 100644 (file)
@@ -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 <deep-sleep-stub>` 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
 -----------------------